Repository: almenscorner/IntuneCD (language: python)

Issue:
Version 1.1.1
- Added ability to split documentation into categories using `-s Y` in `intunecd-startdocumentation`
- Added ability to set max length of output in documentation using `-m {int_value}` in `intunecd-startdocumentation`
- Added backup and documentation of Group Policy Configurations
- Added retry if 503 is encountered during a graph call

Closes #36 
Closes #35 
Closes #33 
Closes #27 


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml
index 9a7788b0..0dca4a1f 100644
--- a/.github/workflows/pypi-publish.yml
+++ b/.github/workflows/pypi-publish.yml
@@ -4,8 +4,8 @@ name: Upload Python Package
 
 on:
   push:
-    branches:
-      - main
+    tags:
+      - '*'
 
 jobs:
   deploy:
diff --git a/.gitignore b/.gitignore
index 1c80b644..b0a47a76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,4 +17,7 @@ share/python-wheels/
 .installed.cfg
 *.egg
 MANIFEST
-.idea/
\ No newline at end of file
+.idea/
+__pycache__/
+.pytest_cache/
+.coverage
\ No newline at end of file
diff --git a/README.md b/README.md
index fd4b794f..def7d354 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
 ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/IntuneCD?style=flat-square)
 ![PyPI](https://img.shields.io/pypi/v/IntuneCD?style=flat-square)
 ![Maintenance](https://img.shields.io/maintenance/yes/2022?style=flat-square)
+![Unit tests](https://github.com/almenscorner/IntuneCD/actions/workflows/unit-test.yml/badge.svg)
+![Publish](https://github.com/almenscorner/IntuneCD/actions/workflows/pypi-publish.yml/badge.svg)
 
 ![IntuneCDlogo2](https://user-images.githubusercontent.com/78877636/156755733-b66a4381-9a9a-4663-9d27-e55a1281e1fa.png)
 
@@ -14,6 +16,19 @@ The main function is to back up configurations from Intune to a Git repository f
 
 The package can also be run standalone outside a pipeline, or in one to only backup data. Since 1.0.4, configurations are also created if they cannot be found. This means this tool could be used in a tenant to tenant migration scenario as well.
 
+## What's new in 1.1.2
+- Added new exclusions for backup and update, it's now possible to exclude certain configurations from backup and update.
+  - Example to exclude in backup: `IntuneCD-startbackup -e assignments AppConfigurations Profiles`
+  - Example to exclude in update: `IntuneCD-startupdate -e AppConfigurations Profiles`
+- Added capabilities to update the IntuneCD frontend with data
+  - Once the frontend is available all that will be needed to update with data is to add `-f <frontend_url>` to startbackup and startupdate command and set the API key in ENV variables.
+- Added ability to configure title, intro, tenant and updated lines in the documentation using a JSON string, example:
+  - `-j "{\"title\": \"demo\", \"intro\": \"demo\", \"tenant\": \"demo\", \"updated\": \"demo\"}"`  
+- Added unit tests
+- Changed deprecated OptionParser to ArgumentParser
+- Improved the documentation
+- Improved overall code readability
+
 ## What's new in 1.1.1
 - Added ability to split documentation into categories using `-s Y` in `intunecd-startdocumentation`
 - Added ability to set max length of output in documentation using `-m {int_value}` in `intunecd-startdocumentation`
@@ -26,28 +41,6 @@ The package can also be run standalone outside a pipeline, or in one to only bac
 - Bugfix for Windows Autopilot profiles not being able to update assignment in a tenant to tenant scenario
 - Bugfix for assignment updates where updating assignments when creating new configurations were not possible if the group does not exist
 
-## What's new in 1.0.9
-- Bugfix where the script exited with "local variable referenced before assignment" if a management intent does not exist
-- Added a new parameter to let you exclude assignments from backups. To exclude assignments from backup, you can now use `-e assignments` when running IntuneCD-startbackup.
-
-## What's new in 1.0.8
-Main focus for this release has been to improve the performance as large setups can take a while to backup/update. With these enhancements, I was able to cut the run time by 80% in most cases
-
-- Added module to use MS Graph batching to get assignments instead on getting them for each configuration individually
-- General code clean up
-- Added new module for getting and updating assignments, the old one was quite messy
-- For some configurations, additional information is appended to the filename, this is because there might be configurations with the same name
-    - App Configurations (appends odata type)
-    - App Protections (appends management type for ios/android and odata type for windows)
-    - Applications (for Windows it now appends the app type e.g. Win32 and version)
-    - Compliance (appends odata type)
-    - Profiles (appends odata type)
-- All configurations are now requested from the start and matched in script with displayName and/or odata type instead of requesting each configuration based on displayName
-- Management intents are now batched using the new batching module
-- Assignments are now batched using the new batching module
-- If 504 or 502 is encountered while getting configurations, the tool will now try again to get the configuration
-- For Windows apps in documentation, detection scripts etc will now have a "Click to expand..." instead of showing the whole script
-
 ## Install this package
 ```python
 pip install IntuneCD
@@ -58,116 +51,30 @@ pip install IntuneCD
 pip install IntuneCD --upgrade
 ```
 
-## What is backed up?
-- Apple Push Notification
-- Apple Volume Purchase Program tokens
-- Application Configuration Policies
-    - Including assignments
-- Application Protection Policies
-    - Including assignments
-- Applications
-    - Including assignments
-- Compliance Policies
-    - Including assignments
-- Device Configurations
-    - Including assignments
-    - For custom macOS and iOS configurations, mobileconfigs are backed up
-- Enrollment profiles
-    - Apple Business Manager
-    - Windows Autopilot
-        - Including assignments
-- Endpoint Security
-    - Including assignments
-    - Security Baselines
-    - Antivirus
-    - Disk Encryption
-    - Firewall
-    - Endpoint Detection and Response
-    - Attack Surface Reduction
-    - Account Protection
-- Filters
-- Managed Google Play
-- Notification Templates
-- Proactive Remediations
-    - Including assignments
-- Partner Connections
-    - Compliance
-    - Management
-    - Remote Assistance
-- Scripts
-    - Including assignments
-    - Powershell
-    - Shell
-- Settings Catalog Policies
-    - Including assignments
-
-## What can be updated?
-Well... most of the above ;)
-
-- Application Configuration Policies
-    - Including assignments
-- Application Protection Policies
-    - Including assignments
-- Compliance Policies
-    - Including assignments
-- Device Configurations
-    - Including assignments
-    - Including custom macOS/iOS .mobileconfigs and custom Windows profiles
-- Enrollment profiles
-    - Apple Business Manager
-    - Windows Autopilot
-        - Including assignments
-- Endpoint Security
-    - Including assignments
-    - Security Baselines
-    - Antivirus
-    - Disk Encryption
-    - Firewall
-    - Endpoint Detection and Response
-    - Attack Surface Reduction
-    - Account Protection
-- Filters
-- Notification Templates
-- Proactive Remediations
-    - Including assignments
-- Scripts
-    - Including assignments
-    - Powershell
-    - Shell
-- Settings Catalog Policies
-    - Including assignments
-
-## What can be created?
-If the configuration the script is looking for cannot be found, it will create it. Supported configurations for creation are:
-
-- Application Configuration Policies
-    - Including assignments
-- Application Protection Policies
-    - Including assignments
-- Compliance Policies
-    - Including assignments
-- Device Configurations
-    - Including assignments
-    - Including custom macOS/iOS .mobileconfigs and custom Windows profiles
-- Endpoint Security
-    - Including assignments
-    - Security Baselines
-    - Antivirus
-    - Disk Encryption
-    - Firewall
-    - Endpoint Detection and Response
-    - Attack Surface Reduction
-    - Account Protection
-- Filters
-- Notification Templates
-- Proactive Remediations
-    - Including assignments
-- Scripts
-    - Including assignments
-    - Powershell
-    - Shell
-- Settings Catalog Policies
-    - Including assignments
+## What is backed up, updated, created and documented?
+| Payload                              |   Back up   | Update | Document |   Create    | Notes                                                                                                                                                     |
+|--------------------------------------|:-----------:|:------:|:--------:|:-----------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Apple Push Notification              |   :tada:    |        |  :tada:  |             |                                                                                                                                                           |
+| Apple Volume Purchase Program tokens |   :tada:    |        |  :tada:  |             |                                                                                                                                                           |
+| Application Configuration Policies   |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           |
+| Application Protection Policies      |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           | 
+| Applications                         |   :tada:    |        |  :tada:  |             |                                                                                                                                                           |
+| Compliance Policies                  |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           |
+| Device Configurations                |   :tada:    | :tada: |  :tada:  |   :tada:    | For custom macOS and iOS configurations,</br>mobileconfigs are backed up                                                                                  |
+| Group Policy Configurations          |   :tada:    |        |  :tada:  |             |                                                                                                                                                           |
+| Enrollment profiles                  | :tada: [^1] | :tada: |  :tada:  | :tada: [^2] |                                                                                                                                                           |
+| Endpoint Security                    |   :tada:    | :tada: |  :tada:  |   :tada:    | Security Baselines</br>Antivirus</br>Disk Encryption</br>Firewall</br>Endpoint Detection and Response</br>Attack Surface Reduction</br>Account Protection |
+| Filters                              |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           |
+| Managed Google Play                  |   :tada:    |        |  :tada:  |             |                                                                                                                                                           |
+| Notification Templates               |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           |
+| Proactive Remediations               |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           |
+| Partner Connections                  |   :tada:    |        |  :tada:  |             | Compliance</br>Management</br>Remote Assistance                                                                                                           |
+| Shell Scripts                        |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           |
+| Powershell Scripts                   |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           |
+| Settings Catalog Policies            |   :tada:    | :tada: |  :tada:  |   :tada:    |                                                                                                                                                           |
+
+[^1]: Only Apple Business Manager and Windows Autopilot profiles are backed up.
+[^2]: Only Windows Autopilot profiles are created.
 
 ## Required Azure AD application Graph API permissions
 - DeviceManagementApps.ReadWrite.All
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 00000000..45c74cd9
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,5 @@
+ignore:
+  - "src/IntuneCD/run_backup.py"
+  - "src/IntuneCD/run_update.py"
+  - "src/IntuneCD/run_documentation.py"
+  - "src/IntuneCD/get_accesstoken.py"
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index 5a73bb1c..931cc995 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = IntuneCD
-version = 1.1.1
+version = 1.1.2
 author = Tobias Almén
 author_email = almenscorner@outlook.com
 description = Tool to backup and update configurations in Intune
diff --git a/src/IntuneCD/backup_AppProtection.py b/src/IntuneCD/backup_AppProtection.py
index 9f5cef21..dfabec04 100644
--- a/src/IntuneCD/backup_AppProtection.py
+++ b/src/IntuneCD/backup_AppProtection.py
@@ -2,64 +2,64 @@
 
 """
 This module backs up all App Protection Polices in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/managedAppPolicies"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/managedAppPolicies"
 
-## Get all App Protection policies and save them in specified path
+# Get all App Protection policies and save them in specified path
 def savebackup(path, output, exclude, token):
-    configpath = path+"/"+"App Protection/"
-    data = makeapirequest(endpoint, token)
+    """
+    Saves all App Protection policies in Intune to a JSON or YAML file.
 
-    assignment_responses = batch_assignment(data,f'deviceAppManagement/','/assignments',token,app_protection=True)
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+    config_count = 0
+    configpath = path + "/" + "App Protection/"
+    data = makeapirequest(ENDPOINT, token)
 
-    ## If profile is ManagedAppConfiguration, skip to next
+    assignment_responses = batch_assignment(
+        data,
+        'deviceAppManagement/',
+        '/assignments',
+        token,
+        app_protection=True)
+
+    # If profile is ManagedAppConfiguration, skip to next
     for profile in data['value']:
         if profile['@odata.type'] == "#microsoft.graph.targetedManagedAppConfiguration":
             continue
-        
+
+        config_count += 1
+
         if "assignments" not in exclude:
-            assignments = get_object_assignment(profile['id'],assignment_responses)
+            assignments = get_object_assignment(
+                profile['id'], assignment_responses)
             if assignments:
                 profile['assignments'] = assignments
-        
-        remove_keys = {'id', 'createdDateTime', 'version',
-                       'lastModifiedDateTime', 'deployedAppCount', 'isAssigned'}
-        for k in remove_keys:
-            profile.pop(k, None)
+
+        profile = remove_keys(profile)
 
         print("Backing up App Protection: " + profile['displayName'])
-        if os.path.exists(configpath) == False:
-            os.mkdir(configpath)
 
         if 'targetedAppManagementLevels' in profile:
-            fname = clean_filename(f"{profile['displayName']}_{profile['targetedAppManagementLevels']}")
+            fname = clean_filename(
+                f"{profile['displayName']}_{profile['targetedAppManagementLevels']}")
         else:
-            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 Protection as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(profile, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+".json", 'w') as jsonFile:
-                json.dump(profile, jsonFile, indent=10)
\ No newline at end of file
+        # Save App Protection as JSON or YAML depending on configured value in
+        # "-o"
+        save_output(output, configpath, fname, profile)
+
+    return config_count
diff --git a/src/IntuneCD/backup_apns.py b/src/IntuneCD/backup_apns.py
index cc5e0746..6d6355e0 100644
--- a/src/IntuneCD/backup_apns.py
+++ b/src/IntuneCD/backup_apns.py
@@ -2,47 +2,40 @@
 
 """
 This module backs up Apple Push Notification setting in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate"
 
-## Get APNs information and save in specified path
+
+# Get APNs information and save in specified path
 def savebackup(path, output, token):
-    configpath = path+"/"+"Apple Push Notification/"
-    data = makeapirequest(endpoint, token)
+    """
+    Save Apple Push Notification setting to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+
+    configpath = path + "/" + "Apple Push Notification/"
+    data = makeapirequest(ENDPOINT, token)
 
     if data:
-        remove_keys = {'id', 'version', 'topicIdentifier', 'certificate'}
-        for k in remove_keys:
-            data.pop(k, None)
+        config_count += 1
+        data = remove_keys(data)
         print("Backing up Apple Push Notification: " + data['appleIdentifier'])
-        if os.path.exists(configpath) == False:
-            os.mkdir(configpath)
 
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(data['appleIdentifier'])
-        ## Save APNs as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(data, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+".json", 'w') as jsonFile:
-                json.dump(data, jsonFile, indent=10)
\ No newline at end of file
+        # Save APNs as JSON or YAML depending on configured value in "-o"
+        save_output(output, configpath, fname, data)
+
+    return config_count
diff --git a/src/IntuneCD/backup_appConfiguration.py b/src/IntuneCD/backup_appConfiguration.py
index eff570e5..e34be8e7 100644
--- a/src/IntuneCD/backup_appConfiguration.py
+++ b/src/IntuneCD/backup_appConfiguration.py
@@ -2,71 +2,68 @@
 
 """
 This module backs up all App Configuration Polices in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations"
-app_endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations"
+APP_ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"
 
-## Get all App Configuration policies and save them in specified path
+
+# Get all App Configuration policies and save them in specified path
 def savebackup(path, output, exclude, token):
-    configpath = path+"/"+"App Configuration/"
-    data = makeapirequest(endpoint, token)
-
-    assignment_responses = batch_assignment(data,f'deviceAppManagement/mobileAppConfigurations/','/assignments',token)
-
-    for profile in data['value']:
-        if "assignments" not in exclude:
-            assignments = get_object_assignment(profile['id'],assignment_responses)
-            if assignments:
-                profile['assignments'] = assignments
-            
-        remove_keys = {'id', 'createdDateTime',
-                       'version', 'lastModifiedDateTime'}
-        for k in remove_keys:
-            profile.pop(k, None)
-
-        ## Get name and type of app on App Configuration Profile
-        app_dict = {}
-        for app_id in profile['targetedMobileApps']:
-            app_data = makeapirequest(app_endpoint + "/" + app_id, token)
-            if app_data:
-                app_dict['appName'] = app_data['displayName']
-                app_dict['type'] = app_data['@odata.type']
-
-        if app_dict:
-            profile.pop('targetedMobileApps')
-            profile['targetedMobileApps'] = app_dict
-
-        print("Backing up App Configuration: " + profile['displayName'])
-
-        if os.path.exists(configpath) == False:
-            os.mkdir(configpath)
-
-        ## Get filename without illegal characters
-        fname = clean_filename(f"{profile['displayName']}_{str(profile['@odata.type'].split('.')[2])}")
-        ## Save App Condiguration as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(profile, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+".json", 'w') as jsonFile:
-                json.dump(profile, jsonFile, indent=10)
\ No newline at end of file
+    """
+    Saves all App Configuration policies in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "App Configuration/"
+    data = makeapirequest(ENDPOINT, token)
+
+    if data['value']:
+
+        assignment_responses = batch_assignment(
+            data, 'deviceAppManagement/mobileAppConfigurations/', '/assignments', token)
+
+        for profile in data['value']:
+            config_count += 1
+            if "assignments" not in exclude:
+                assignments = get_object_assignment(
+                    profile['id'], assignment_responses)
+                if assignments:
+                    profile['assignments'] = assignments
+
+            profile = remove_keys(profile)
+
+            # Get name and type of app on App Configuration Profile
+            app_dict = {}
+            for app_id in profile['targetedMobileApps']:
+                app_data = makeapirequest(APP_ENDPOINT + "/" + app_id, token)
+                if app_data:
+                    app_dict['appName'] = app_data['displayName']
+                    app_dict['type'] = app_data['@odata.type']
+
+            if app_dict:
+                profile.pop('targetedMobileApps')
+                profile['targetedMobileApps'] = app_dict
+
+            print("Backing up App Configuration: " + profile['displayName'])
+
+            # Get filename without illegal characters
+            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)
+
+    return config_count
diff --git a/src/IntuneCD/backup_appleEnrollmentProfile.py b/src/IntuneCD/backup_appleEnrollmentProfile.py
index f3631d90..c7ee3f44 100644
--- a/src/IntuneCD/backup_appleEnrollmentProfile.py
+++ b/src/IntuneCD/backup_appleEnrollmentProfile.py
@@ -2,57 +2,56 @@
 
 """
 This module backs up all Apple Enrollment Profiles in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_request
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings/"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings/"
 
-## Get all Apple Enrollment Profiles and save them in specified path
+# Get all Apple Enrollment Profiles and save them in specified path
 def savebackup(path, output, token):
-    configpath = path+"/"+"Enrollment Profiles/Apple/"
-    data = makeapirequest(endpoint, token)
-
-    profile_ids = []
-    for profile in data['value']:
-        profile_ids.append(profile['id'])
-
-    batch_profile_data = batch_request(profile_ids,'deviceManagement/depOnboardingSettings/','/enrollmentProfiles',token)
-
-    for profile in batch_profile_data:
-        for value in profile['value']:
-            remove_keys = {'id', 'createdDateTime', 'version',
-                        'lastModifiedDateTime', 'isDefault'}
-            for k in remove_keys:
-                value.pop(k, None)
-
-            print("Backing up Apple enrollment profile: " + value['displayName'])
-            if os.path.exists(configpath) == False:
-                os.makedirs(configpath)
-
-            ## Get filename without illegal characters
-            fname = clean_filename(value['displayName'])
-            ## Save Apple Enrollment Pofile as JSON or YAML depending on configured value in "-o"
-            if output != "json":
-                with open(configpath+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(value, yamlFile, sort_keys=False,
-                            default_flow_style=False)
-            else:
-                with open(configpath+fname+".json", 'w') as jsonFile:
-                    json.dump(value, jsonFile, indent=10)
\ No newline at end of file
+    """
+    Saves all Apple Enrollment Profiles in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Enrollment Profiles/Apple/"
+    data = makeapirequest(ENDPOINT, token)
+
+    if data['value']:
+        profile_ids = []
+        for profile in data['value']:
+            profile_ids.append(profile['id'])
+
+        batch_profile_data = batch_request(
+            profile_ids,
+            'deviceManagement/depOnboardingSettings/',
+            '/enrollmentProfiles',
+            token)
+
+        for profile in batch_profile_data:
+            config_count += 1
+            for value in profile['value']:
+                value = remove_keys(value)
+
+                print(
+                    "Backing up Apple enrollment profile: " +
+                    value['displayName'])
+
+                # Get filename without illegal characters
+                fname = clean_filename(value['displayName'])
+                # Save Apple Enrollment Profile as JSON or YAML depending on
+                # configured value in "-o"
+                save_output(output, configpath, fname, value)
+
+    return config_count
diff --git a/src/IntuneCD/backup_applications.py b/src/IntuneCD/backup_applications.py
index daffe1e8..9aa3c4c9 100644
--- a/src/IntuneCD/backup_applications.py
+++ b/src/IntuneCD/backup_applications.py
@@ -1,106 +1,119 @@
 #!/usr/bin/env python3
 
 """
-This module backs up all applications in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
+This module backs up all App Protection Policies in Intune.
 """
 
-import json
-import os
-import yaml
 import re
 
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-q_param = {"$filter":"(microsoft.graph.managedApp/appAvailability) eq null or (microsoft.graph.managedApp/appAvailability) eq 'lineOfBusiness' or isAssigned eq true"}
-endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"
+# Set MS Graph endpoint
+q_param = {
+    "$filter": "(microsoft.graph.managedApp/appAvailability) eq null or (microsoft.graph.managedApp/appAvailability) "
+               "eq 'lineOfBusiness' or isAssigned eq true"}
+ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"
+
+
+def match(platform, input) -> bool:
+    """
+    This function matches the platform from the @odata.type.
+
+    :param platform: The platform to match
+    :param input: The OData type
+    :return: False if no match, True if match
+    """
 
-## Function to match platform from @odata.type
-def match (platform,input) -> bool:
     string = f'.*{platform}.*$'
     pattern = re.compile(string)
-    match =  pattern.match(input, re.IGNORECASE)
-    if match:
-        return True
-    else:
-        return False
+    match = pattern.match(input, re.IGNORECASE)
+    return bool(match)
+
+
+# Get all applications and save them in specified path
+def savebackup(path, output, exclude, token):
+    """
+    Saves all applications in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
 
-## Get all applications and save them in specified path
-def savebackup(path,output,exclude,token):
-    configpath = f'{path}/Applications/'
+    config_count = 0
 
-    data = makeapirequest(endpoint,token,q_param)
-    assignment_responses = batch_assignment(data,f'deviceAppManagement/mobileApps/','/assignments',token)
+    data = makeapirequest(ENDPOINT, token, q_param)
+    assignment_responses = batch_assignment(
+        data, 'deviceAppManagement/mobileApps/', '/assignments', token)
 
     for app in data['value']:
         app_name = ""
         platform = ""
+        config_count += 1
 
         if "assignments" not in exclude:
-            assignments = get_object_assignment(app['id'],assignment_responses)
+            assignments = get_object_assignment(
+                app['id'], assignment_responses)
             if assignments:
                 app['assignments'] = assignments
 
-        remove_keys={'id','createdDateTime','version','lastModifiedDateTime','description'}
-        for k in remove_keys:
-            app.pop(k, None)
-        
-        ## If app type is VPP, add Apple ID to the name as the app can exist multiple times
+        app = remove_keys(app)
+        app.pop('description', None)
+
+        # If app type is VPP, add Apple ID to the name as the app can exist
+        # multiple times
         if app['@odata.type'] == '#microsoft.graph.iosVppApp':
-            app_name = app['displayName']+'_iOSVppApp_'+str(app['vppTokenAppleId'].split('@')[0])
+            app_name = app['displayName'] + '_iOSVppApp_' + \
+                str(app['vppTokenAppleId'].split('@')[0])
         elif app['@odata.type'] == '#microsoft.graph.macOsVppApp':
-            app_name = app['displayName']+'_macOSVppApp_'+str(app['vppTokenAppleId'].split('@')[0])
-        ## If app type is Win32 or MSI, add version to the name as multiple versions can exist
+            app_name = app['displayName'] + '_macOSVppApp_' + \
+                str(app['vppTokenAppleId'].split('@')[0])
+        # If app type is Win32 or MSI, add version to the name as multiple
+        # versions can exist
         elif app['@odata.type'] == '#microsoft.graph.win32LobApp':
             if app['displayVersion'] is None:
-                app_name = app['displayName']+'_Win32'
+                app_name = app['displayName'] + '_Win32'
             else:
-                app_name = app['displayName']+'_Win32_'+str(app['displayVersion']).replace('.','_')
+                app_name = app['displayName'] + '_Win32_' + \
+                    str(app['displayVersion']).replace('.', '_')
         elif app['@odata.type'] == '#microsoft.graph.windowsMobileMSI':
-            app_name = app['displayName']+'_WinMSI_'+str(app['productVersion']).replace('.','_')
-        ## If app is not VPP, Win32 or MSI only add the app type to the name
+            app_name = app['displayName'] + '_WinMSI_' + \
+                str(app['productVersion']).replace('.', '_')
+        # If app is not VPP, Win32 or MSI only add the app type to the name
         else:
-            app_name = app['displayName']+'_'+str(app['@odata.type'].split('.')[2])
+            app_name = app['displayName'] + '_' + \
+                str(app['@odata.type'].split('.')[2])
 
-        ## Get application platform
-        if match('ios',str(app['@odata.type']).lower()) is True:
+        # Get application platform
+        if match('ios', str(app['@odata.type']).lower()) is True:
             platform = 'iOS'
-        if match('macos',str(app['@odata.type']).lower()) is True:
+        if match('macos', str(app['@odata.type']).lower()) is True:
             platform = 'macOS'
-        if match('android',str(app['@odata.type']).lower()) is True:
+        if match('android', str(app['@odata.type']).lower()) is True:
             platform = 'Android'
-        if match('windows',str(app['@odata.type']).lower()) is True:
+        if match('windows', str(app['@odata.type']).lower()) is True:
             platform = 'Windows'
-        if match('microsoft',str(app['@odata.type']).lower()) is True:
+        if match('microsoft', str(app['@odata.type']).lower()) is True:
             platform = 'Windows'
-        if match('win32',str(app['@odata.type']).lower()) is True:
+        if match('win32', str(app['@odata.type']).lower()) is True:
             platform = 'Windows'
-        if match('office',str(app['@odata.type']).lower()) is True:
-            platform = 'Office Suite'        
+        if match('office', str(app['@odata.type']).lower()) is True:
+            platform = 'Office Suite'
         if app['@odata.type'] == '#microsoft.graph.webApp':
             platform = 'Web App'
 
         print(f"Backing up Application: {app['displayName']}")
-        if os.path.exists(f'{configpath}/{platform}')==False:
-            os.makedirs(f'{configpath}/{platform}')
 
-        ## Get filename without illegal characters
+        configpath = f'{path}/Applications/{platform}/'
+
+        # Get filename without illegal characters
         fname = clean_filename(app_name)
-        ## Save Applications as JSON or YAML depending on configured value in "-o"
-        if output != 'json':
-            with open(f'{configpath}{platform}/{fname}.yaml','w') as yamlFile:
-                yaml.dump(app,yamlFile,sort_keys=False,default_flow_style=False)
-        else:
-            with open(f'{configpath}{platform}/{fname}.json','w') as jsonFile:
-                json.dump(app,jsonFile,indent=10)
\ No newline at end of file
+
+        save_output(output, configpath, fname, app)
+
+    return config_count
diff --git a/src/IntuneCD/backup_assignmentFilters.py b/src/IntuneCD/backup_assignmentFilters.py
index 3965e786..eb2750d6 100644
--- a/src/IntuneCD/backup_assignmentFilters.py
+++ b/src/IntuneCD/backup_assignmentFilters.py
@@ -2,49 +2,41 @@
 
 """
 This module backs up all Filters in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters"
 
-## Get all Filters and save them in specified path
+# Get all Filters and save them in specified path
 def savebackup(path, output, token):
-    configpath = path+"/"+"Filters/"
-    data = makeapirequest(endpoint, token)
+    """
+    Saves all Filter in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Filters/"
+    data = makeapirequest(ENDPOINT, token)
 
     if data:
         for assign_filter in data['value']:
-            remove_keys = {'id', 'createdDateTime',
-                           'version', 'lastModifiedDateTime'}
-            for k in remove_keys:
-                assign_filter.pop(k, None)
+            config_count += 1
+            assign_filter = remove_keys(assign_filter)
             print("Backing up Filter: " + assign_filter['displayName'])
-            if os.path.exists(configpath) == False:
-                os.mkdir(configpath)
 
-            ## Get filename without illegal characters
+            # Get filename without illegal characters
             fname = clean_filename(assign_filter['displayName'])
-            ## Save Filters as JSON or YAML depending on configured value in "-o"
-            if output != "json":
-                with open(configpath+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(assign_filter, yamlFile,
-                              sort_keys=False, default_flow_style=False)
-            else:
-                with open(configpath+fname+".json", 'w') as jsonFile:
-                    json.dump(assign_filter, jsonFile, indent=10)
\ No newline at end of file
+            # Save Filters as JSON or YAML depending on configured value in
+            # "-o"
+            save_output(output, configpath, fname, assign_filter)
+
+    return config_count
diff --git a/src/IntuneCD/backup_compliance.py b/src/IntuneCD/backup_compliance.py
index 69d6ac55..fedbebd1 100644
--- a/src/IntuneCD/backup_compliance.py
+++ b/src/IntuneCD/backup_compliance.py
@@ -2,65 +2,59 @@
 
 """
 This module backs up all Compliance Polices in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies"
 
-## Get all Compliance policies and save them in specified path
+
+# Get all Compliance policies and save them in specified path
 def savebackup(path, output, exclude, token):
-    configpath = path+"/"+"Compliance Policies/Policies/"
+    """
+    Saves all Compliance policies in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Compliance Policies/Policies/"
     q_param = {
         "$expand": "scheduledActionsForRule($expand=scheduledActionConfigurations)"}
-    data = makeapirequest(endpoint, token, q_param)
+    data = makeapirequest(ENDPOINT, token, q_param)
 
-    assignment_responses = batch_assignment(data,f'deviceManagement/deviceCompliancePolicies/','/assignments',token)
+    assignment_responses = batch_assignment(
+        data, 'deviceManagement/deviceCompliancePolicies/', '/assignments', token)
 
     for policy in data['value']:
+        config_count += 1
         print("Backing up compliance policy: " + policy['displayName'])
 
         if "assignments" not in exclude:
-            assignments = get_object_assignment(policy['id'],assignment_responses)
+            assignments = get_object_assignment(
+                policy['id'], assignment_responses)
             if assignments:
                 policy['assignments'] = assignments
 
-        remove_keys = {'id', 'createdDateTime', 'version', 'lastModifiedDateTime', '@odata.context',
-                       'scheduledActionConfigurations@odata.context', 'scheduledActionsForRule@odata.context'}
-        for k in remove_keys:
-            policy.pop(k, None)
+            policy = remove_keys(policy)
             for rule in policy['scheduledActionsForRule']:
-                rule.pop(k, None)
+                remove_keys(rule)
             if policy['scheduledActionsForRule']:
                 for scheduled_config in policy['scheduledActionsForRule'][0]['scheduledActionConfigurations']:
-                    scheduled_config.pop(k, None)
-
-        if os.path.exists(configpath) == False:
-            os.makedirs(configpath)
+                    remove_keys(scheduled_config)
 
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(policy['displayName'])
-        ## Save Compliance policy as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+'_'+str(policy['@odata.type'].split('.')[2])+".yaml", 'w') as yamlFile:
-                yaml.dump(policy, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+'_'+str(policy['@odata.type'].split('.')[2])+".json", 'w') as jsonFile:
-                json.dump(policy, jsonFile, indent=10)
\ No newline at end of file
+        # Save Compliance policy as JSON or YAML depending on configured value
+        # in "-o"
+        save_output(output, configpath, f'{fname}_' + str(policy['@odata.type'].split('.')[2]), policy)
+
+    return config_count
diff --git a/src/IntuneCD/backup_compliancePartner.py b/src/IntuneCD/backup_compliancePartner.py
index 48cec6a4..d3f41492 100644
--- a/src/IntuneCD/backup_compliancePartner.py
+++ b/src/IntuneCD/backup_compliancePartner.py
@@ -2,52 +2,44 @@
 
 """
 This module backs up all Compliance Partners in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/complianceManagementPartners"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/complianceManagementPartners"
 
-## Get all Compliance Partners and save them in specified path
+# Get all Compliance Partners and save them in specified path
 def savebackup(path, output, token):
-    configpath = path+"/"+"Partner Connections/Compliance/"
-    data = makeapirequest(endpoint, token)
+    """
+    Saves all Compliance Partners in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Partner Connections/Compliance/"
+    data = makeapirequest(ENDPOINT, token)
 
     for partner in data['value']:
         if partner['partnerState'] == "unknown":
             continue
-        else:
-            print("Backing up Compliance Partner: " + partner['displayName'])
-
-            remove_keys = {'id', 'createdDateTime', 'version', 'lastModifiedDateTime'}
-            for k in remove_keys:
-                partner.pop(k, None)
-
-            if os.path.exists(configpath) == False:
-                os.makedirs(configpath)
-
-            ## Get filename without illegal characters
-            fname = clean_filename(partner['displayName'])
-            ## Save Compliance policy as JSON or YAML depending on configured value in "-o"
-            if output != "json":
-                with open(configpath+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(partner, yamlFile, sort_keys=False,
-                            default_flow_style=False)
-            else:
-                with open(configpath+fname+".json", 'w') as jsonFile:
-                    json.dump(partner, jsonFile, indent=10)
\ No newline at end of file
+
+        config_count += 1
+        print("Backing up Compliance Partner: " + partner['displayName'])
+
+        partner = remove_keys(partner)
+
+        # Get filename without illegal characters
+        fname = clean_filename(partner['displayName'])
+        # Save Compliance policy as JSON or YAML depending on configured
+        # value in "-o"
+        save_output(output, configpath, fname, partner)
+
+    return config_count
diff --git a/src/IntuneCD/backup_configurationPolicies.py b/src/IntuneCD/backup_configurationPolicies.py
index ce1366d1..48d14a75 100644
--- a/src/IntuneCD/backup_configurationPolicies.py
+++ b/src/IntuneCD/backup_configurationPolicies.py
@@ -2,67 +2,69 @@
 
 """
 This module backs up all Configuration Policies in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment, batch_request, get_object_details
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph base endpoint
+BASE_ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement"
 
-## Set MS Graph base endpoint
-baseEndpoint = "https://graph.microsoft.com/beta/deviceManagement"
 
-## Get all Configuration Policies and save them in specified path
+# Get all Configuration Policies and save them in specified path
 def savebackup(path, output, exclude, token):
-    configpath = path+"/"+"Settings Catalog/"
-    policies = makeapirequest(baseEndpoint + "/configurationPolicies", token)
+    """
+    Saves all Configuration Policies in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Settings Catalog/"
+    policies = makeapirequest(BASE_ENDPOINT + "/configurationPolicies", token)
     policy_ids = []
     for policy in policies['value']:
         policy_ids.append(policy['id'])
 
-    assignment_responses = batch_assignment(policies,f'deviceManagement/configurationPolicies/','/assignments',token)
-    policy_settings_batch = batch_request(policy_ids,'deviceManagement/configurationPolicies/','/settings',token)
+    assignment_responses = batch_assignment(
+        policies,
+        'deviceManagement/configurationPolicies/',
+        '/assignments',
+        token)
+    policy_settings_batch = batch_request(
+        policy_ids,
+        'deviceManagement/configurationPolicies/',
+        '/settings',
+        token)
 
     for policy in policies['value']:
+        config_count += 1
         name = policy['name']
         print("Backing up configuration policy: " + name)
-        if os.path.exists(configpath) == False:
-            os.mkdir(configpath)
 
-        settings = get_object_details(policy['id'],policy_settings_batch)
+        settings = get_object_details(policy['id'], policy_settings_batch)
 
         if settings:
             policy['settings'] = settings
 
         if "assignments" not in exclude:
-            assignments = get_object_assignment(policy['id'],assignment_responses)
+            assignments = get_object_assignment(
+                policy['id'], assignment_responses)
             if assignments:
                 policy['assignments'] = assignments
 
-        remove_keys = {'id', 'createdDateTime',
-                       'version', 'lastModifiedDateTime'}
-        for k in remove_keys:
-            policy.pop(k, None)
+        policy = remove_keys(policy)
 
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(name)
-        ## Save Configuration Policy as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(policy, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+".json", 'w') as jsonFile:
-                json.dump(policy, jsonFile, indent=10)
\ No newline at end of file
+        # Save Configuration Policy as JSON or YAML depending on configured
+        # value in "-o"
+        save_output(output, configpath, fname, policy)
+
+    return config_count
diff --git a/src/IntuneCD/backup_groupPolicyConfiguration.py b/src/IntuneCD/backup_groupPolicyConfiguration.py
index 111360e5..6fd632b3 100644
--- a/src/IntuneCD/backup_groupPolicyConfiguration.py
+++ b/src/IntuneCD/backup_groupPolicyConfiguration.py
@@ -2,40 +2,39 @@
 
 """
 This module backs up Group Policy Configurations in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import base64
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations"
 
-## Get all Group Policy Configurations and save them in specified path
+# Get all Group Policy Configurations and save them in specified path
 def savebackup(path, output, exclude, token):
+    """
+    Saves all Group Policy Configurations in Intune to a JSON or YAML file.
 
-    configpath = path+"/"+"Group Policy Configurations/"
-    data = makeapirequest(endpoint, token)
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
 
-    assignment_responses = batch_assignment(data,f'deviceManagement/groupPolicyConfigurations/','/assignments',token)
+    config_count = 0
+    configpath = path + "/" + "Group Policy Configurations/"
+    data = makeapirequest(ENDPOINT, token)
 
-    for profile in data['value']:
+    assignment_responses = batch_assignment(
+        data, 'deviceManagement/groupPolicyConfigurations/', '/assignments', token)
 
-        definition_endpoint = f"{endpoint}/{profile['id']}/definitionValues?$expand=definition"
+    for profile in data['value']:
+        config_count += 1
+        definition_endpoint = f"{ENDPOINT}/{profile['id']}/definitionValues?$expand=definition"
         # Get definitions
         definitions = makeapirequest(definition_endpoint, token)
 
@@ -44,32 +43,25 @@ def savebackup(path, output, exclude, token):
             profile['definitionValues'] = definitions['value']
 
             for definition in profile['definitionValues']:
-                presentation_endpoint = f"{endpoint}/{profile['id']}/definitionValues/{definition['id']}/presentationValues?$expand=presentation"
+                presentation_endpoint = \
+                    f"{ENDPOINT}/{profile['id']}/definitionValues/{definition['id']}/" \
+                    f"presentationValues?$expand=presentation "
                 presentation = makeapirequest(presentation_endpoint, token)
                 definition['presentationValues'] = presentation['value']
 
         if "assignments" not in exclude:
-            assignments = get_object_assignment(profile['id'],assignment_responses)
+            assignments = get_object_assignment(
+                profile['id'], assignment_responses)
             if assignments:
                 profile['assignments'] = assignments
 
-        pid = profile['id']
-        remove_keys = {'id', 'createdDateTime', 'version',
-                       'lastModifiedDateTime', 'sourceId', 'supportsScopeTags'}
-        for k in remove_keys:
-            profile.pop(k, None)
+        profile = remove_keys(profile)
 
         print("Backing up profile: " + profile['displayName'])
-        if os.path.exists(configpath) == False:
-            os.makedirs(configpath)
 
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(profile['displayName'])
 
-        if output != "json":
-            with open(configpath+'/'+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(profile, yamlFile, sort_keys=False,
-                            default_flow_style=False)
-        else:
-            with open(configpath+'/'+fname+".json", 'w') as jsonFile:
-                json.dump(profile, jsonFile, indent=10)
\ No newline at end of file
+        save_output(output, configpath, fname, profile)
+
+    return config_count
diff --git a/src/IntuneCD/backup_managedGPlay.py b/src/IntuneCD/backup_managedGPlay.py
index f43b3981..ca4c65cb 100644
--- a/src/IntuneCD/backup_managedGPlay.py
+++ b/src/IntuneCD/backup_managedGPlay.py
@@ -2,48 +2,41 @@
 
 """
 This module backs up Managed Google Play setting in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/androidManagedStoreAccountEnterpriseSettings"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/androidManagedStoreAccountEnterpriseSettings"
 
-## Get Managed Google Play information and save in specified path
+# Get Managed Google Play information and save in specified path
 def savebackup(path, output, token):
+    """
+    Saves Managed Google Play information in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
     configpath = f'{path}/Managed Google Play/'
-    data = makeapirequest(endpoint, token)
+    data = makeapirequest(ENDPOINT, token)
 
     if data:
-        remove_keys = {'id', 'companyCodes'}
-        for k in remove_keys:
-            data.pop(k, None)
+        config_count += 1
+        data = remove_keys(data)
         print("Backing up Managed Google Play: " +
               data['ownerUserPrincipalName'])
-        if os.path.exists(configpath) == False:
-            os.mkdir(configpath)
 
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(data['ownerUserPrincipalName'])
-        ## Save Managed Google Play as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(f'{configpath}{fname}.yaml', 'w') as yamlFile:
-                yaml.dump(data, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(f'{configpath}{fname}.json', 'w') as jsonFile:
-                json.dump(data, jsonFile, indent=10)
\ No newline at end of file
+        # Save Managed Google Play as JSON or YAML depending on configured
+        # value in "-o"
+        save_output(output, configpath, fname, data)
+
+    return config_count
diff --git a/src/IntuneCD/backup_managementIntents.py b/src/IntuneCD/backup_managementIntents.py
index dc2625ec..133aaa1b 100644
--- a/src/IntuneCD/backup_managementIntents.py
+++ b/src/IntuneCD/backup_managementIntents.py
@@ -2,67 +2,63 @@
 
 """
 This module backs up all Intents in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_intents, get_object_assignment, batch_assignment
+from .save_output import save_output
+
+# Set MS Graph base endpoint
+BASE_ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement"
+TEMPLATE_ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/templates"
 
-## Set MS Graph base endpoint
-baseEndpoint = "https://graph.microsoft.com/beta/deviceManagement"
-template_endpoint = "https://graph.microsoft.com/beta/deviceManagement/templates"
 
-## Get all Intents and save them in specified path
+# Get all Intents and save them in specified path
 def savebackup(path, output, exclude, token):
-    configpath = path+"/"+"Management Intents/"
-    intents = makeapirequest(baseEndpoint + "/intents", token)
-    templates = makeapirequest(template_endpoint,token)
+    """
+    Saves all Intents in Intune to a JSON or YAML file.
 
-    assignment_responses = batch_assignment(intents,f'deviceManagement/intents/','/assignments',token)
-    intent_responses = batch_intents(intents,token)
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Management Intents/"
+    intents = makeapirequest(BASE_ENDPOINT + "/intents", token)
+    templates = makeapirequest(TEMPLATE_ENDPOINT, token)
+
+    assignment_responses = batch_assignment(
+        intents, 'deviceManagement/intents/', '/assignments', token)
+    intent_responses = batch_intents(intents, token)
 
     if intent_responses:
         for intent_value in intent_responses['value']:
+            config_count += 1
             print("Backing up Intent: " + intent_value['displayName'])
 
             for template in templates['value']:
                 if intent_value['templateId'] == template['id']:
                     template_type = template['displayName']
 
-            configpath = path+"/"+"Management Intents/" + \
+            configpath = path + "/" + "Management Intents/" + \
                 template_type + "/"
-            if os.path.exists(configpath) == False:
-                os.makedirs(configpath)
 
             if "assignments" not in exclude:
-                assignments = get_object_assignment(intent_value['id'],assignment_responses)
+                assignments = get_object_assignment(
+                    intent_value['id'], assignment_responses)
                 if assignments:
                     intent_value['assignments'] = assignments
 
             for setting in intent_value['settingsDelta']:
-                setting.pop('id',None)
-            intent_value.pop('id',None)
+                setting.pop('id', None)
+            intent_value.pop('id', None)
 
-            ## Get filename without illegal characters
+            # Get filename without illegal characters
             fname = clean_filename(intent_value['displayName'])
-            ## Save Intent as JSON or YAML depending on configured value in "-o"
-            if output != "json":
-                with open(configpath+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(intent_value, yamlFile,
-                                sort_keys=False, default_flow_style=False)
-            else:
-                with open(configpath+fname+".json", 'w') as jsonFile:
-                    json.dump(intent_value, jsonFile, indent=10)
\ No newline at end of file
+            # Save Intent as JSON or YAML depending on configured value in "-o"
+            save_output(output, configpath, fname, intent_value)
+
+    return config_count
diff --git a/src/IntuneCD/backup_managementPartner.py b/src/IntuneCD/backup_managementPartner.py
index 85837da1..78b99b92 100644
--- a/src/IntuneCD/backup_managementPartner.py
+++ b/src/IntuneCD/backup_managementPartner.py
@@ -2,52 +2,44 @@
 
 """
 This module backs up all Management Partners in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementPartners"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementPartners"
 
-## Get all Management Partners and save them in specified path
+# Get all Management Partners and save them in specified path
 def savebackup(path, output, token):
-    configpath = path+"/"+"Partner Connections/Management/"
-    data = makeapirequest(endpoint, token)
+    """
+    Saves Management Partner information in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Partner Connections/Management/"
+    data = makeapirequest(ENDPOINT, token)
 
     for partner in data['value']:
         if partner['isConfigured'] is False:
             continue
-        else:
-            print("Backing up Management Partner: " + partner['displayName'])
-            
-            remove_keys = {'id', 'createdDateTime', 'version', 'lastModifiedDateTime'}
-            for k in remove_keys:
-                partner.pop(k, None)
-
-            if os.path.exists(configpath) == False:
-                os.makedirs(configpath)
-
-            ## Get filename without illegal characters
-            fname = clean_filename(partner['displayName'])
-            ## Save Compliance policy as JSON or YAML depending on configured value in "-o"
-            if output != "json":
-                with open(configpath+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(partner, yamlFile, sort_keys=False,
-                            default_flow_style=False)
-            else:
-                with open(configpath+fname+".json", 'w') as jsonFile:
-                    json.dump(partner, jsonFile, indent=10)
\ No newline at end of file
+
+        config_count += 1
+        print("Backing up Management Partner: " + partner['displayName'])
+
+        partner = remove_keys(partner)
+
+        # Get filename without illegal characters
+        fname = clean_filename(partner['displayName'])
+        # Save Compliance policy as JSON or YAML depending on configured
+        # value in "-o"
+        save_output(output, configpath, fname, partner)
+
+    return config_count
diff --git a/src/IntuneCD/backup_notificationTemplate.py b/src/IntuneCD/backup_notificationTemplate.py
index cb8a9375..791572b8 100644
--- a/src/IntuneCD/backup_notificationTemplate.py
+++ b/src/IntuneCD/backup_notificationTemplate.py
@@ -2,59 +2,49 @@
 
 """
 This module backs up all Notification Templates in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/notificationMessageTemplates"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/notificationMessageTemplates"
 
-## Get all Notification Templates and save them in specified path
+
+# Get all Notification Templates and save them in specified path
 def savebackup(path, output, token):
-    configpath = path+"/"+"Compliance Policies/Message Templates/"
+    """
+    Saves all Notification Templates in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Compliance Policies/Message Templates/"
     q_param = "?$expand=localizedNotificationMessages"
-    data = makeapirequest(endpoint, token, q_param)
+    data = makeapirequest(ENDPOINT, token, q_param)
 
     for template in data['value']:
+        config_count += 1
         print("Backing up Notification message template: " +
               template['displayName'])
         q_param = "?$expand=localizedNotificationMessages"
         template_data = makeapirequest(
-            endpoint + "/" + template['id'], token, q_param)
+            ENDPOINT + "/" + template['id'], token, q_param)
 
-        remove_keys = {'id', 'createdDateTime',
-                       'version', 'lastModifiedDateTime'}
-        for k in remove_keys:
-            template_data.pop(k, None)
+        template_data = remove_keys(template_data)
 
         for locale in template_data['localizedNotificationMessages']:
-            for k in remove_keys:
-                locale.pop(k, None)
-
-        if os.path.exists(configpath) == False:
-            os.makedirs(configpath)
+            remove_keys(locale)
 
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(template_data['displayName'])
-        ## Save App Condiguration as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(template_data, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+".json", 'w') as jsonFile:
-                json.dump(template_data, jsonFile, indent=10)
\ No newline at end of file
+        # Save Notification template as JSON or YAML depending on configured
+        # value in "-o"
+        save_output(output, configpath, fname, template_data)
+
+    return config_count
diff --git a/src/IntuneCD/backup_powershellScripts.py b/src/IntuneCD/backup_powershellScripts.py
index 365ceebd..6ba67943 100644
--- a/src/IntuneCD/backup_powershellScripts.py
+++ b/src/IntuneCD/backup_powershellScripts.py
@@ -2,70 +2,69 @@
 
 """
 This module backs up all Powershell scripts in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
 import os
 import base64
-import yaml
 
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment, batch_request
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts/"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts/"
 
-## Get all Powershell scripts and save them in specified path
+# Get all Powershell scripts and save them in specified path
 def savebackup(path, output, exclude, token):
-    configpath = path+"/"+"Scripts/Powershell/"
-    data = makeapirequest(endpoint, token)
-    script_ids = []
-    for script in data['value']:
-        script_ids.append(script['id'])
+    """
+    Saves all Powershell scripts in Intune to a JSON or YAML file and script files.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Scripts/Powershell/"
+    data = makeapirequest(ENDPOINT, token)
+    if data['value']:
+        script_ids = []
+        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,f'deviceManagement/intents/','/assignments',token)
-    script_data_responses = batch_request(script_ids,f'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)
+                if assignments:
+                    script_data['assignments'] = assignments
 
-    for script_data in script_data_responses:
-        if "assignments" not in exclude:
-            assignments = get_object_assignment(script_data['id'],assignment_responses)
-            if assignments:
-                script_data['assignments'] = assignments
+            script_data = remove_keys(script_data)
 
-        remove_keys = {'id', 'createdDateTime',
-                       'version', 'lastModifiedDateTime'}
-        for k in remove_keys:
-            script_data.pop(k, None)
+            print("Backing up Powershell script: " + script_data['displayName'])
 
-        print("Backing up Powershell script: " + script_data['displayName'])
-        if os.path.exists(configpath) == False:
-            os.makedirs(configpath)
+            # Get filename without illegal characters
+            fname = clean_filename(script_data['displayName'])
+            # Save Powershell script as JSON or YAML depending on configured value
+            # in "-o"
+            save_output(output, configpath, fname, script_data)
 
-        ## Get filename without illegal characters
-        fname = clean_filename(script_data['displayName'])
-        ## Save Powershell script as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(script_data, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+".json", 'w') as jsonFile:
-                json.dump(script_data, jsonFile, indent=10)
+            # Save Powershell script data to the script data folder
+            if not os.path.exists(configpath + "Script Data/"):
+                os.makedirs(configpath + "Script Data/")
+            decoded = base64.b64decode(
+                script_data['scriptContent']).decode('utf-8')
+            f = open(configpath + "Script Data/" + script_data['fileName'], 'w')
+            f.write(decoded)
 
-        ## Save Powershell script data to the script data folder
-        if os.path.exists(configpath + "Script Data/") == False:
-            os.makedirs(configpath + "Script Data/")
-        decoded = base64.b64decode(
-            script_data['scriptContent']).decode('utf-8')
-        f = open(configpath+"Script Data/"+script_data['fileName'], 'w')
-        f.write(decoded)
\ No newline at end of file
+    return config_count
diff --git a/src/IntuneCD/backup_proactiveRemediation.py b/src/IntuneCD/backup_proactiveRemediation.py
index b67cb881..040b19a1 100644
--- a/src/IntuneCD/backup_proactiveRemediation.py
+++ b/src/IntuneCD/backup_proactiveRemediation.py
@@ -1,82 +1,85 @@
 #!/usr/bin/env python3
 
 """
-This module backs up all Proactive Remediations in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
+This module backs up all Proactive Remediation in Intune.
 """
 
-import json
 import os
 import base64
-import yaml
 
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment, batch_request
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts"
 
-## Get all Proactive Remediations and save them in specified path
+
+# Get all Proactive Remediation and save them in specified path
 def savebackup(path, output, exclude, token):
+    """
+    Saves all Proactive Remediation in Intune to a JSON or YAML file and script files.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
     configpath = f'{path}/Proactive Remediations/'
-    data = makeapirequest(endpoint, token)
-    pr_ids = []
-    for script in data['value']:
-        pr_ids.append(script['id'])
-
-    assignment_responses = batch_assignment(data,f'deviceManagement/deviceHealthScripts/','/assignments',token)
-    pr_data_responses = batch_request(pr_ids,f'deviceManagement/deviceHealthScripts/','',token)
-
-    for pr_details in pr_data_responses:
-        if "Microsoft" not in pr_details['publisher']:
-            
-            if "assignments" not in exclude:
-                assignments = get_object_assignment(pr_details['id'],assignment_responses)
-                if assignments:
-                    pr_details['assignments'] = assignments
-                
-            remove_keys = {'id', 'createdDateTime', 'version',
-                           'lastModifiedDateTime', 'isGlobalScript', 'highestAvailableVersion'}
-            for k in remove_keys:
-                pr_details.pop(k, None)
-
-            print(f"Backing up Proactive Remediation: {pr_details['displayName']}")
-            if os.path.exists(configpath) == False:
-                os.makedirs(configpath)
-
-            ## Get filename without illegal characters
-            fname = clean_filename(pr_details['displayName'])
-
-            ## Save Proactive Remediation as JSON or YAML depending on configured value in "-o"
-            if output != 'json':
-                with open(f"{configpath}{fname}.yaml", 'w') as yamlFile:
-                    yaml.dump(pr_details, yamlFile, sort_keys=False,
-                              default_flow_style=False)
-            else:
-                with open(f"{configpath}{fname}.json", 'w') as jsonFile:
-                    json.dump(pr_details, jsonFile, indent=10)
-
-            if os.path.exists(f'{configpath}/Script Data') == False:
-                os.makedirs(f'{configpath}/Script Data')
-
-            ## Save detection script to the Script Data folder
-            decoded = base64.b64decode(
-                pr_details['detectionScriptContent']).decode('utf-8')
-            f = open(
-                f"{configpath}/Script Data/{pr_details['displayName']}_DetectionScript.ps1", 'w')
-            f.write(decoded)
-            ## Save remediation script to the Script Data folder
-            decoded = base64.b64decode(
-                pr_details['remediationScriptContent']).decode('utf-8')
-            f = open(
-                f"{configpath}/Script Data/{pr_details['displayName']}_RemediationScript.ps1", 'w')
-            f.write(decoded)
\ No newline at end of file
+    data = makeapirequest(ENDPOINT, token)
+    if data['value']:
+        pr_ids = []
+        for script in data['value']:
+            pr_ids.append(script['id'])
+
+        assignment_responses = batch_assignment(
+            data, 'deviceManagement/deviceHealthScripts/', '/assignments', token)
+        pr_data_responses = batch_request(
+            pr_ids, 'deviceManagement/deviceHealthScripts/', '', token)
+
+        for pr_details in pr_data_responses:
+            if "Microsoft" not in pr_details['publisher']:
+                config_count += 1
+                if "assignments" not in exclude:
+                    assignments = get_object_assignment(
+                        pr_details['id'], assignment_responses)
+                    if assignments:
+                        pr_details['assignments'] = assignments
+
+                pr_details = remove_keys(pr_details)
+
+                print(
+                    f"Backing up Proactive Remediation: {pr_details['displayName']}")
+
+                # Get filename without illegal characters
+                fname = clean_filename(pr_details['displayName'])
+
+                # Save Proactive Remediation as JSON or YAML depending on
+                # configured value in "-o"
+                save_output(output, configpath, fname, pr_details)
+
+                if not os.path.exists(f'{configpath}/Script Data'):
+                    os.makedirs(f'{configpath}/Script Data')
+
+                # Save detection script to the Script Data folder
+                config_count += 1
+                decoded = base64.b64decode(
+                    pr_details['detectionScriptContent']).decode('utf-8')
+                f = open(
+                    f"{configpath}/Script Data/{pr_details['displayName']}_DetectionScript.ps1",
+                    'w')
+                f.write(decoded)
+                # Save remediation script to the Script Data folder
+                config_count += 1
+                decoded = base64.b64decode(
+                    pr_details['remediationScriptContent']).decode('utf-8')
+                f = open(
+                    f"{configpath}/Script Data/{pr_details['displayName']}_RemediationScript.ps1",
+                    'w')
+                f.write(decoded)
+
+    return config_count
diff --git a/src/IntuneCD/backup_profiles.py b/src/IntuneCD/backup_profiles.py
index f528339f..9ce8af5c 100644
--- a/src/IntuneCD/backup_profiles.py
+++ b/src/IntuneCD/backup_profiles.py
@@ -2,96 +2,90 @@
 
 """
 This module backs up Device Configurations in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
 import os
 import base64
-import yaml
-import re
 
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations"
 
-def match (platform,input) -> bool:
-    string = f'.*{platform}.*$'
-    pattern = re.compile(string)
-    match =  pattern.match(input, re.IGNORECASE)
-    if match:
-        return True
-    else:
-        return False
 
-## Get all Device Configurations and save them in specified path
+# Get all Device Configurations and save them in specified path
 def savebackup(path, output, exclude, token):
+    """
+    Saves all Device Configurations in Intune to a JSON or YAML file and custom macOS/iOS to .mobileconfig.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
 
-    configpath = path+"/"+"Device Configurations/"
-    data = makeapirequest(endpoint, token)
+    config_count = 0
+    configpath = path + "/" + "Device Configurations/"
+    data = makeapirequest(ENDPOINT, token)
 
-    assignment_responses = batch_assignment(data,f'deviceManagement/deviceConfigurations/','/assignments',token)
+    assignment_responses = batch_assignment(
+        data, 'deviceManagement/deviceConfigurations/', '/assignments', token)
 
     for profile in data['value']:
+        config_count += 1
         if "assignments" not in exclude:
-            assignments = get_object_assignment(profile['id'],assignment_responses)
+            assignments = get_object_assignment(
+                profile['id'], assignment_responses)
             if assignments:
-                profile['assignments'] = assignments   
+                profile['assignments'] = assignments
 
         pid = profile['id']
-        remove_keys = {'id', 'createdDateTime', 'version',
-                       'lastModifiedDateTime', 'sourceId', 'supportsScopeTags'}
-        for k in remove_keys:
-            profile.pop(k, None)
+        profile = remove_keys(profile)
 
         print("Backing up profile: " + profile['displayName'])
-        if os.path.exists(configpath) == False:
-            os.makedirs(configpath)
 
-        ## Get filename without illegal characters
-        fname = clean_filename(f"{profile['displayName']}_{str(profile['@odata.type']).split('.')[2]}")
+        # Get filename without illegal characters
+        fname = clean_filename(
+            f"{profile['displayName']}_{str(profile['@odata.type']).split('.')[2]}")
 
-        ## If profile is custom macOS or iOS, decode the payload
-        if ((profile['@odata.type'] == "#microsoft.graph.macOSCustomConfiguration") or (profile['@odata.type'] == "#microsoft.graph.iosCustomConfiguration")):
+        # If profile is custom macOS or iOS, decode the payload
+        if ((profile['@odata.type'] == "#microsoft.graph.macOSCustomConfiguration")
+                or (profile['@odata.type'] == "#microsoft.graph.iosCustomConfiguration")):
             decoded = base64.b64decode(profile['payload']).decode('utf-8')
 
-            if os.path.exists(configpath + '/' + "mobileconfig/") == False:
+            if not os.path.exists(configpath + '/' + "mobileconfig/"):
                 os.makedirs(configpath + '/' + "mobileconfig/")
-            ## Save decoded payload as .mobileconfig
+            # Save decoded payload as .mobileconfig
+            config_count += 1
             f = open(configpath + '/' + "mobileconfig/" +
                      profile['payloadFileName'], 'w')
             f.write(decoded)
-            ## Save Device Configuration as JSON or YAML depending on configured value in "-o"
-            if output != "json":
-                with open(configpath+'/'+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(profile, yamlFile, sort_keys=False,
-                              default_flow_style=False)
-            else:
-                with open(configpath+'/'+fname+".json", 'w') as jsonFile:
-                    json.dump(profile, jsonFile, indent=10)
-
-        ## If Device Configuration is custom Win10 and the OMA settings are encrypted, get them in plain text
+            # Save Device Configuration as JSON or YAML depending on configured
+            # value in "-o"
+            save_output(output, configpath, fname, profile)
+
+        # If Device Configuration is custom Win10 and the OMA settings are
+        # encrypted, get them in plain text
         elif profile['@odata.type'] == "#microsoft.graph.windows10CustomConfiguration":
             if profile['omaSettings']:
                 if profile['omaSettings'][0]['isEncrypted'] is True:
 
                     omas = []
                     for setting in profile['omaSettings']:
-                        if setting['isEncrypted'] == True:
+                        if setting['isEncrypted']:
                             decoded_oma = {}
                             oma_value = makeapirequest(
-                                endpoint + "/" + pid + "/getOmaSettingPlainTextValue(secretReferenceValueId='" + setting['secretReferenceValueId'] + "')", token)
+                                ENDPOINT +
+                                "/" +
+                                pid +
+                                "/getOmaSettingPlainTextValue(secretReferenceValueId='" +
+                                setting['secretReferenceValueId'] +
+                                "')",
+                                token)
                             decoded_oma['@odata.type'] = setting['@odata.type']
                             decoded_oma['displayName'] = setting['displayName']
                             decoded_oma['description'] = setting['description']
@@ -101,27 +95,17 @@ def savebackup(path, output, exclude, token):
                             decoded_oma['secretReferenceValueId'] = None
                             decoded_omas = decoded_oma
                             omas.append(decoded_omas)
-                        elif setting['isEncrypted'] == False:
-                            omas.append(setting)
 
                     profile.pop('omaSettings')
                     profile['omaSettings'] = omas
 
-            ## Save Device Configuration as JSON or YAML depending on configured value in "-o"
-            if output != "json":
-                with open(configpath+'/'+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(profile, yamlFile, sort_keys=False,
-                              default_flow_style=False)
-            else:
-                with open(configpath+'/'+fname+".json", 'w') as jsonFile:
-                    json.dump(profile, jsonFile, indent=10)
+            # Save Device Configuration as JSON or YAML depending on configured
+            # value in "-o"
+            save_output(output, configpath, fname, profile)
 
-        ## If Device Configuration are not custom, save it as as JSON or YAML depending on configured value in "-o"
+        # If Device Configuration are not custom, save it as JSON or YAML
+        # depending on configured value in "-o"
         else:
-            if output != "json":
-                with open(configpath+'/'+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(profile, yamlFile, sort_keys=False,
-                              default_flow_style=False)
-            else:
-                with open(configpath+'/'+fname+".json", 'w') as jsonFile:
-                    json.dump(profile, jsonFile, indent=10)
\ No newline at end of file
+            save_output(output, configpath, fname, profile)
+
+    return config_count
diff --git a/src/IntuneCD/backup_remoteAssistancePartner.py b/src/IntuneCD/backup_remoteAssistancePartner.py
index 4a368770..6113a840 100644
--- a/src/IntuneCD/backup_remoteAssistancePartner.py
+++ b/src/IntuneCD/backup_remoteAssistancePartner.py
@@ -2,52 +2,46 @@
 
 """
 This module backs up all Remote Assistance Partners in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/remoteAssistancePartners"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/remoteAssistancePartners"
 
-## Get all Remote Assistance Partners and save them in specified path
+# Get all Remote Assistance Partners and save them in specified path
 def savebackup(path, output, token):
-    configpath = path+"/"+"Partner Connections/Remote Assistance/"
-    data = makeapirequest(endpoint, token)
+    """
+    Saves all Remote Assistance Partners in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Partner Connections/Remote Assistance/"
+    data = makeapirequest(ENDPOINT, token)
 
     for partner in data['value']:
         if partner['onboardingStatus'] == "notOnboarded":
             continue
-        else:
-            print("Backing up Remote Assistance Partner: " + partner['displayName'])
-
-            remove_keys = {'id', 'createdDateTime', 'version', 'lastModifiedDateTime'}
-            for k in remove_keys:
-                partner.pop(k, None)
-
-            if os.path.exists(configpath) == False:
-                os.makedirs(configpath)
-
-            ## Get filename without illegal characters
-            fname = clean_filename(partner['displayName'])
-            ## Save Compliance policy as JSON or YAML depending on configured value in "-o"
-            if output != "json":
-                with open(configpath+fname+".yaml", 'w') as yamlFile:
-                    yaml.dump(partner, yamlFile, sort_keys=False,
-                            default_flow_style=False)
-            else:
-                with open(configpath+fname+".json", 'w') as jsonFile:
-                    json.dump(partner, jsonFile, indent=10)
\ No newline at end of file
+
+        config_count += 1
+        print(
+            "Backing up Remote Assistance Partner: " +
+            partner['displayName'])
+
+        partner = remove_keys(partner)
+
+        # Get filename without illegal characters
+        fname = clean_filename(partner['displayName'])
+        # Save Compliance policy as JSON or YAML depending on configured
+        # value in "-o"
+        save_output(output, configpath, fname, partner)
+
+    return config_count
diff --git a/src/IntuneCD/backup_shellScripts.py b/src/IntuneCD/backup_shellScripts.py
index d56d1621..72e38a64 100644
--- a/src/IntuneCD/backup_shellScripts.py
+++ b/src/IntuneCD/backup_shellScripts.py
@@ -2,72 +2,66 @@
 
 """
 This module backs up all Shell scripts in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
 import os
 import base64
-import yaml
 
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
 from .graph_batch import batch_assignment, get_object_assignment, batch_request
+from .save_output import save_output
+from .remove_keys import remove_keys
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceShellScripts/"
+ASSIGNMENT_ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceShellScripts/"
-assignment_endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
 
-## Get all Shell scripts and save them in specified path
+# Get all Shell scripts and save them in specified path
 def savebackup(path, output, exclude, token):
-    configpath = path+"/"+"Scripts/Shell/"
-    data = makeapirequest(endpoint, token)
+    """
+    Saves all Shell scripts in Intune to a JSON or YAML file and script files.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
+    configpath = path + "/" + "Scripts/Shell/"
+    data = makeapirequest(ENDPOINT, token)
     script_ids = []
     for script in data['value']:
         script_ids.append(script['id'])
 
-    assignment_responses = batch_assignment(data,f'deviceManagement/deviceManagementScripts/','/assignments',token)
-    script_data_responses = batch_request(script_ids,f'deviceManagement/deviceShellScripts/','',token)
+    assignment_responses = batch_assignment(data, 'deviceManagement/deviceManagementScripts/', '/assignments', token)
+    script_data_responses = batch_request(script_ids, 'deviceManagement/deviceShellScripts/', '', 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
 
-        remove_keys = {'id', 'createdDateTime',
-                       'version', 'lastModifiedDateTime'}
-        for k in remove_keys:
-            script_data.pop(k, None)
+        script_data = remove_keys(script_data)
 
         print("Backing up Shell script: " + script_data['displayName'])
-        if os.path.exists(configpath) == False:
-            os.makedirs(configpath)
 
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(script_data['displayName'])
 
-        ## Save Shell script as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(script_data, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+".json", 'w') as jsonFile:
-                json.dump(script_data, jsonFile, indent=10)
-
-        ## Save Shell script data to the script data folder
-        if os.path.exists(configpath + "Script Data/") == False:
+        # Save Shell script as JSON or YAML depending on configured value in "-o"
+        save_output(output, configpath, fname, script_data)
+
+        # Save Shell script data to the script data folder
+        if not os.path.exists(configpath + "Script Data/"):
             os.makedirs(configpath + "Script Data/")
         decoded = base64.b64decode(
             script_data['scriptContent']).decode('utf-8')
-        f = open(configpath+"Script Data/"+script_data['fileName'], 'w')
-        f.write(decoded)
\ No newline at end of file
+        f = open(configpath + "Script Data/" + script_data['fileName'], 'w')
+        f.write(decoded)
+
+    return config_count
diff --git a/src/IntuneCD/backup_vppTokens.py b/src/IntuneCD/backup_vppTokens.py
index 02b0178c..2b649ef1 100644
--- a/src/IntuneCD/backup_vppTokens.py
+++ b/src/IntuneCD/backup_vppTokens.py
@@ -2,52 +2,42 @@
 
 """
 This module backs up all VPP tokens in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/vppTokens"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/vppTokens"
 
-## Get all VPP tokens and save them in specified path
+
+# Get all VPP tokens and save them in specified path
 def savebackup(path, output, token):
+    """
+    Save all VPP tokens in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param token: Token to use for authenticating the request
+    """
+
+    config_count = 0
     configpath = f'{path}/Apple VPP Tokens/'
-    data = makeapirequest(endpoint, token)
+    data = makeapirequest(ENDPOINT, token)
 
     for vpp_token in data['value']:
+        config_count += 1
         token_name = vpp_token['displayName']
-        remove_keys = {'id', 'createdDateTime', 'version',
-                       'lastModifiedDateTime', 'token', 'lastSyncDateTime'}
-        for k in remove_keys:
-            vpp_token.pop(k, None)
+        vpp_token = remove_keys(vpp_token)
 
         print(f'Backing up VPP token: {token_name}')
 
-        if os.path.exists(configpath) == False:
-            os.makedirs(configpath)
-
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(token_name)
 
-        ## Save token as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(f'{configpath}{fname}.yaml', 'w') as yamlFile:
-                yaml.dump(vpp_token, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(f'{configpath}{fname}.json', 'w') as jsonFile:
-                json.dump(vpp_token, jsonFile, indent=10)
\ No newline at end of file
+        # Save token as JSON or YAML depending on configured value in "-o"
+        save_output(output, configpath, fname, vpp_token)
+
+    return config_count
diff --git a/src/IntuneCD/backup_windowsEnrollmentProfile.py b/src/IntuneCD/backup_windowsEnrollmentProfile.py
index b6d3d32a..c668aba5 100644
--- a/src/IntuneCD/backup_windowsEnrollmentProfile.py
+++ b/src/IntuneCD/backup_windowsEnrollmentProfile.py
@@ -2,57 +2,56 @@
 
 """
 This module backs up all Windows Enrollment Profiles in Intune.
-
-Parameters
-----------
-path : str
-    The path to save the backup to
-output : str
-    The format the backup will be saved as
-token : str
-    The token to use for authenticating the request
 """
 
-import json
-import os
-import yaml
-
 from .clean_filename import clean_filename
 from .graph_request import makeapirequest
-from .graph_batch import batch_assignment,get_object_assignment
+from .graph_batch import batch_assignment, get_object_assignment
+from .save_output import save_output
+from .remove_keys import remove_keys
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles"
 
-## Get all Windows Enrollment Profiles and save them in specified path
+
+# Get all Windows Enrollment Profiles and save them in specified path
 def savebackup(path, output, exclude, token):
-    configpath = path+"/"+"Enrollment Profiles/Windows/"
-    data = makeapirequest(endpoint, token)
+    """
+    Saves all Windows Enrollment Profiles in Intune to a JSON or YAML file.
+
+    :param path: Path to save the backup to
+    :param output: Format the backup will be saved as
+    :param exclude: If "assignments" is in the list, it will not back up the assignments
+    :param token: Token to use for authenticating the request
+    """
 
-    assignment_responses = batch_assignment(data,'deviceManagement/windowsAutopilotDeploymentProfiles/','/assignments',token)
+    config_count = 0
+    configpath = path + "/" + "Enrollment Profiles/Windows/"
+    data = makeapirequest(ENDPOINT, token)
+
+    assignment_responses = batch_assignment(
+        data,
+        'deviceManagement/windowsAutopilotDeploymentProfiles/',
+        '/assignments',
+        token)
 
     for profile in data['value']:
+        config_count += 1
         if "assignments" not in exclude:
-            assignments = get_object_assignment(profile['id'],assignment_responses)
+            assignments = get_object_assignment(
+                profile['id'], assignment_responses)
             if assignments:
                 profile['assignments'] = assignments
-        
-        remove_keys = {'id', 'createdDateTime',
-                       'version', 'lastModifiedDateTime'}
-        for k in remove_keys:
-            profile.pop(k, None)
+
+        profile = remove_keys(profile)
+
         print("Backing up Autopilot enrollment profile: " +
               profile['displayName'])
-        if os.path.exists(configpath) == False:
-            os.makedirs(configpath)
 
-        ## Get filename without illegal characters
+        # Get filename without illegal characters
         fname = clean_filename(profile['displayName'])
-        ## Save Windows Enrollment Profile as JSON or YAML depending on configured value in "-o"
-        if output != "json":
-            with open(configpath+fname+".yaml", 'w') as yamlFile:
-                yaml.dump(profile, yamlFile, sort_keys=False,
-                          default_flow_style=False)
-        else:
-            with open(configpath+fname+".json", 'w') as jsonFile:
-                json.dump(profile, jsonFile, indent=10)
\ No newline at end of file
+        # Save Windows Enrollment Profile as JSON or YAML depending on
+        # configured value in "-o"
+        save_output(output, configpath, fname, profile)
+
+    return config_count
diff --git a/src/IntuneCD/check_file.py b/src/IntuneCD/check_file.py
new file mode 100644
index 00000000..d7fbc73f
--- /dev/null
+++ b/src/IntuneCD/check_file.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+
+"""
+This module is used to check if a file is a YAML or JSON file.
+"""
+
+import os
+
+
+def check_file(configpath, filename):
+    """
+    Check if file is YAML or JSON, if true return the file, if not return False.
+
+    :param configpath: The path to the config file
+    :param filename: The name of the file
+    :return: The file or False
+    """
+
+    file = os.path.join(configpath, filename)
+    if file.endswith(".yaml"):
+        return file
+    if file.endswith(".json"):
+        return file
+    else:
+        return False
diff --git a/src/IntuneCD/clean_filename.py b/src/IntuneCD/clean_filename.py
index 433f0281..f2c65660 100644
--- a/src/IntuneCD/clean_filename.py
+++ b/src/IntuneCD/clean_filename.py
@@ -1,16 +1,21 @@
+#!/usr/bin/env python3
+
 """
 This module is used to remove illegal characters from strings before saving files.
-
-Parameters
-----------
-filenasme : str
-    The name of file to save
 """
 
+
 def clean_filename(filename):
-    remove_characters = "/\:*?<>|"
+    """
+    This function removes illegal characters from strings.
+
+    :param filename: The name of file to save
+    :return: The cleaned filename
+    """
+
+    remove_characters = "/\\:*?<>|"
     if type(filename) != str:
         filename = str(filename)
     for character in remove_characters:
         filename = filename.replace(character, "_")
-    return filename
\ No newline at end of file
+    return filename
diff --git a/src/IntuneCD/documentation_functions.py b/src/IntuneCD/documentation_functions.py
index 0a878032..0240a9a6 100644
--- a/src/IntuneCD/documentation_functions.py
+++ b/src/IntuneCD/documentation_functions.py
@@ -1,16 +1,7 @@
 #!/usr/bin/env python3
 
 """
-This module contains all functions to create the markdown tables.
-
-Parameters
-----------
-configpath : str
-    The path to where the backup files are saved
-outpath : str
-    The path to save the markdown document to
-header : str
-    Header of the configuration being documented
+This module contains all functions for the documentation.
 """
 
 import yaml
@@ -20,13 +11,27 @@
 
 from pytablewriter import MarkdownTableWriter
 
+
 def md_file(outpath):
-    if os.path.exists(f'{outpath}') == False:
+    """
+    This function creates the markdown file.
+
+    :param outpath: The path to save the Markdown document to
+    """
+    if not os.path.exists(f'{outpath}'):
         open(outpath, 'w+').close()
     else:
         open(outpath, 'w').close()
 
+
 def write_table(data):
+    """
+    This function creates the markdown table.
+
+    :param data: The data to be written to the table
+    :return: The Markdown table writer
+    """
+
     writer = MarkdownTableWriter(
         headers=['setting', 'value'],
         value_matrix=data
@@ -34,7 +39,14 @@ def write_table(data):
 
     return writer
 
+
 def assignment_table(data):
+    """
+    This function creates the Markdown assignments table.
+
+    :param data: The data to be written to the table
+    :return: The Markdown table writer
+    """
 
     def write_assignment_table(data, headers):
         writer = MarkdownTableWriter(
@@ -76,14 +88,28 @@ def write_assignment_table(data, headers):
 
     return table
 
+
 def remove_characters(string):
+    """
+    This function removes characters from the string.
+    :param string: The string to be cleaned
+    :return: The cleaned string
+    """
+
     remove_chars = '#@}{]["'
     for char in remove_chars:
         string = string.replace(char, "")
 
     return string
 
+
 def clean_list(data):
+    """
+    This function cleans the list.
+    :param data: The list to be cleaned
+    :return: The cleaned list
+    """
+
     values = []
     liststr = ","
     for item in data:
@@ -94,8 +120,8 @@ def clean_list(data):
                     if i.isdigit():
                         string = i
                 if type(i) is list:
-                        string = i+liststr
-                        string = remove_characters(string)
+                    string = i + liststr
+                    string = remove_characters(string)
                 if type(i) is dict:
                     for k, v in i.items():
                         if type(v) is str and len(v) > 200:
@@ -123,140 +149,165 @@ def clean_list(data):
 
     return values
 
-def document_configs(configpath, outpath, header, max_length, split):
 
-    ## If configurations path exists, continue
-    if os.path.exists(configpath) == True:
-        if split == True:
+def document_configs(configpath, outpath, header, max_length, split):
+    """
+    This function documents the configuration.
+
+    :param configpath: The path to where the backup files are saved
+    :param outpath: The path to save the Markdown document to
+    :param header: Header of the configuration being documented
+    :param max_length: The maximum length of the configuration to write to the Markdown document
+    :param split: Split documentation into multiple files
+    """
+
+    # If configurations path exists, continue
+    if os.path.exists(configpath):
+        if split:
             outpath = configpath + "/" + header + ".md"
             md_file(outpath)
         with open(outpath, 'a') as md:
-            md.write('# '+header+'\n')
+            md.write('# ' + header + '\n')
 
         pattern = configpath + "*/*"
         for filename in glob.glob(pattern, recursive=True):
             if filename.endswith(".md"):
                 continue
-            ## If path is Directory, skip
+            # If path is Directory, skip
             if os.path.isdir(filename):
                 continue
             # If file is .DS_Store, skip
             if filename == ".DS_Store":
                 continue
 
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data and set query parameter
             with open(filename) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                    elif filename.endswith(".json"):
-                        f = open(filename)
-                        repo_data = json.load(f)
-
-                    # Create assignments table
-                    assignments_table = ""
-                    assignments_table = assignment_table(repo_data)
-                    repo_data.pop('assignments', None)
-
-                    description = ""
-                    if 'description' in repo_data:
-                        if repo_data['description'] != None:
-                            description = repo_data['description']
-                            repo_data.pop('description')
-
-                    # Write configuration markdown table
-                    config_table_list = []
-                    for key, value in zip(repo_data.keys(),clean_list(repo_data.values())):
-                        if max_length:
-                            if value and type(value) == str and len(value) > max_length:
-                                value = "Value too long to display"
-                        config_table_list.append([key, value])
-                    config_table = write_table(config_table_list)
-
-                    # Write data to file
-                    with open(outpath, 'a') as md:
-                        if "displayName" in repo_data:
-                            md.write('## '+repo_data['displayName']+'\n')
-                        if "name" in repo_data:
-                            md.write('## '+repo_data['name']+'\n')
-                        if description:
-                            md.write(f'Description: {description} \n')
-                        if assignments_table:
-                            md.write('### Assignments \n')
-                            md.write(str(assignments_table)+'\n')
-                        md.write(str(config_table)+'\n')
-
-def document_management_intents(configpath, outpath,header,split):
-    ## If configurations path exists, continue
-    if os.path.exists(configpath) ==True:
-        if split == True:
+                if filename.endswith(".yaml"):
+                    data = json.dumps(yaml.safe_load(f))
+                    repo_data = json.loads(data)
+                elif filename.endswith(".json"):
+                    f = open(filename)
+                    repo_data = json.load(f)
+
+                # Create assignments table
+                assignments_table = ""
+                assignments_table = assignment_table(repo_data)
+                repo_data.pop('assignments', None)
+
+                description = ""
+                if 'description' in repo_data:
+                    if repo_data['description'] is not None:
+                        description = repo_data['description']
+                        repo_data.pop('description')
+
+                # Write configuration Markdown table
+                config_table_list = []
+                for key, value in zip(repo_data.keys(), clean_list(repo_data.values())):
+                    if max_length:
+                        if value and type(value) == str and len(value) > max_length:
+                            value = "Value too long to display"
+                    config_table_list.append([key, value])
+                config_table = write_table(config_table_list)
+
+                # Write data to file
+                with open(outpath, 'a') as md:
+                    if "displayName" in repo_data:
+                        md.write('## ' + repo_data['displayName'] + '\n')
+                    if "name" in repo_data:
+                        md.write('## ' + repo_data['name'] + '\n')
+                    if description:
+                        md.write(f'Description: {description} \n')
+                    if assignments_table:
+                        md.write('### Assignments \n')
+                        md.write(str(assignments_table) + '\n')
+                    md.write(str(config_table) + '\n')
+
+
+def document_management_intents(configpath, outpath, header, split):
+    """
+    This function documents the management intents.
+
+    :param configpath: The path to where the backup files are saved
+    :param outpath: The path to save the Markdown document to
+    :param header: Header of the configuration being documented
+    :param split: Split documentation into multiple files
+    """
+
+    # If configurations path exists, continue
+    if os.path.exists(configpath):
+        if split:
             outpath = configpath + "/" + header + ".md"
             md_file(outpath)
         with open(outpath, 'a') as md:
-            md.write('# '+header+'\n')
+            md.write('# ' + header + '\n')
 
         pattern = configpath + "*/*"
         for filename in glob.glob(pattern, recursive=True):
-            ## If path is Directory, skip
+            # If path is Directory, skip
             if os.path.isdir(filename):
                 continue
             # If file is .DS_Store, skip
             if filename == ".DS_Store":
                 continue
 
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data and set query parameter
             with open(filename) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                    elif filename.endswith(".json"):
-                        f = open(filename)
-                        repo_data = json.load(f)
-
-                    # Create assignments table
-                    assignments_table = ""
-                    assignments_table = assignment_table(repo_data)
-                    repo_data.pop('assignments', None)
-
-                    intent_settings_list = []
-
-                    for setting in repo_data['settingsDelta']:
-                        intent_settings_list.append([setting['definitionId'].split("_")[1],
-                                                    str(remove_characters(setting['valueJson']))])
-
-                    repo_data.pop('settingsDelta')
-
-                    description = ""
-                    if 'description' in repo_data:
-                        if repo_data['description'] != None:
-                            description = repo_data['description']
-                            repo_data.pop('description')
-
-                    intent_table_list = []
-
-                    for key, value in zip(repo_data.keys(),clean_list(repo_data.values())):
-                        intent_table_list.append([key, value])
-
-                    table = intent_table_list + intent_settings_list
-
-                    config_table = write_table(table)
-                    # Write data to file
-                    with open(outpath, 'a') as md:
-                        if "displayName" in repo_data:
-                            md.write('## '+repo_data['displayName']+'\n')
-                        if "name" in repo_data:
-                            md.write('## '+repo_data['name']+'\n')
-                        if description:
-                            md.write(f'Description: {description} \n')
-                        if assignments_table:
-                            md.write('### Assignments \n')
-                            md.write(str(assignments_table)+'\n')
-                        md.write(str(config_table)+'\n')
+                if filename.endswith(".yaml"):
+                    data = json.dumps(yaml.safe_load(f))
+                    repo_data = json.loads(data)
+                elif filename.endswith(".json"):
+                    f = open(filename)
+                    repo_data = json.load(f)
+
+                # Create assignments table
+                assignments_table = ""
+                assignments_table = assignment_table(repo_data)
+                repo_data.pop('assignments', None)
+
+                intent_settings_list = []
+                for setting in repo_data['settingsDelta']:
+                    intent_settings_list.append([setting['definitionId'].split("_")[1],
+                                                 str(remove_characters(setting['valueJson']))])
+
+                repo_data.pop('settingsDelta')
+
+                description = ""
+                if 'description' in repo_data:
+                    if repo_data['description'] is not None:
+                        description = repo_data['description']
+                        repo_data.pop('description')
+
+                intent_table_list = []
+
+                for key, value in zip(repo_data.keys(), clean_list(repo_data.values())):
+                    intent_table_list.append([key, value])
+
+                table = intent_table_list + intent_settings_list
+
+                config_table = write_table(table)
+                # Write data to file
+                with open(outpath, 'a') as md:
+                    if "displayName" in repo_data:
+                        md.write('## ' + repo_data['displayName'] + '\n')
+                    if "name" in repo_data:
+                        md.write('## ' + repo_data['name'] + '\n')
+                    if description:
+                        md.write(f'Description: {description} \n')
+                    if assignments_table:
+                        md.write('### Assignments \n')
+                        md.write(str(assignments_table) + '\n')
+                    md.write(str(config_table) + '\n')
+
 
 def get_md_files():
+    """
+    This function gets the Markdown files in the current directory.
+    :return: List of Markdown files
+    """
+
     md_files = []
-    patterns = ["*/*.md","*/*/*.md","*/*/*/*.md"]
+    patterns = ["*/*.md", "*/*/*.md", "*/*/*/*.md"]
     for pattern in patterns:
         for filename in glob.glob(pattern, recursive=True):
             md_files.append(f'./{filename}')
-    return md_files
\ No newline at end of file
+    return md_files
diff --git a/src/IntuneCD/get_accesstoken.py b/src/IntuneCD/get_accesstoken.py
index d4045322..c292f29d 100644
--- a/src/IntuneCD/get_accesstoken.py
+++ b/src/IntuneCD/get_accesstoken.py
@@ -1,26 +1,26 @@
 #!/usr/bin/env python3
 
 """
-This module is used to get an access token to MS Graph.
-
-Parameters
-----------
-TENANT_NAME : str
-    The name of the Azure tenant
-CLIENT_ID : str
-    The ID of the registered Azure AD application
-CLIENT_SECRET : str
-    Secret of the registered Azure AD application
+This module contains the functions used to get the access token for MS Graph.
 """
 
 from adal import AuthenticationContext
 
-from __main__ import *
 
-def obtain_accesstoken(TENANT_NAME,CLIENT_ID,CLIENT_SECRET,resource):
+def obtain_accesstoken(TENANT_NAME, CLIENT_ID, CLIENT_SECRET, resource):
+    """
+    This function is used to get an access token to MS Graph.
+
+    :param TENANT_NAME: The name of the Azure tenant
+    :param CLIENT_ID: The ID of the registered Azure AD application
+    :param CLIENT_SECRET: Secret of the registered Azure AD application
+    :param resource: The resource to get an access token for
+    :return: The access token
+    """
+
     auth_context = AuthenticationContext('https://login.microsoftonline.com/' +
-        TENANT_NAME)
+                                         TENANT_NAME)
     token = auth_context.acquire_token_with_client_credentials(
-        resource=resource,client_id=CLIENT_ID,
+        resource=resource, client_id=CLIENT_ID,
         client_secret=CLIENT_SECRET)
-    return token
\ No newline at end of file
+    return token
diff --git a/src/IntuneCD/get_authparams.py b/src/IntuneCD/get_authparams.py
index f398df5f..f7971963 100644
--- a/src/IntuneCD/get_authparams.py
+++ b/src/IntuneCD/get_authparams.py
@@ -1,14 +1,7 @@
+#!/usr/bin/env python3
+
 """
-This module authenticates to MS Graph and returns the access token.
-
-Parameters
-----------
-mode : str
-    The mode used when using this tool
-localauth : str
-    Path to dict with keys to authenticate
-tenant : str
-    Which tenant to authenticate to, PROD or DEV
+This module is used to get the access token for the tenant.
 """
 
 import json
@@ -17,23 +10,41 @@
 
 resource = "https://graph.microsoft.com"
 
-def getAuth(mode,localauth,tenant):
-    if mode == 'devtoprod':
 
+def getAuth(mode, localauth, tenant):
+    """
+    This function authenticates to MS Graph and returns the access token.
+
+    :param mode: The mode used when using this tool
+    :param localauth: Path to dict with keys to authenticate
+    :param tenant: Which tenant to authenticate to, PROD or DEV
+    :return: The access token
+    """
+
+    if mode == 'devtoprod':
         if localauth:
             with open(localauth) as json_data:
                 auth_dict = json.load(json_data)
-            tenant_TENANT_NAME = auth_dict['params'][tenant+'_TENANT_NAME']
-            tenant_CLIENT_ID = auth_dict['params'][tenant+'_CLIENT_ID']
-            tenant_CLIENT_SECRET = auth_dict['params'][tenant+'_CLIENT_SECRET']
+            tenant_TENANT_NAME = auth_dict['params'][tenant + '_TENANT_NAME']
+            tenant_CLIENT_ID = auth_dict['params'][tenant + '_CLIENT_ID']
+            tenant_CLIENT_SECRET = auth_dict['params'][tenant +
+                                                       '_CLIENT_SECRET']
         else:
-            tenant_TENANT_NAME = os.environ.get(tenant+"_TENANT_NAME")
-            tenant_CLIENT_ID = os.environ.get(tenant+"_CLIENT_ID")
-            tenant_CLIENT_SECRET = os.environ.get(tenant+"_CLIENT_SECRET")
-        if ((tenant_TENANT_NAME is None) or (tenant_CLIENT_ID is None) or (tenant_CLIENT_SECRET is None)):
-            raise Exception("One or more os.environ variables for " + tenant + " not set")
+            tenant_TENANT_NAME = os.environ.get(tenant + "_TENANT_NAME")
+            tenant_CLIENT_ID = os.environ.get(tenant + "_CLIENT_ID")
+            tenant_CLIENT_SECRET = os.environ.get(tenant + "_CLIENT_SECRET")
+        if ((tenant_TENANT_NAME is None) or (tenant_CLIENT_ID is None)
+                or (tenant_CLIENT_SECRET is None)):
+            raise Exception(
+                "One or more os.environ variables for " +
+                tenant +
+                " not set")
         else:
-            token = obtain_accesstoken(tenant_TENANT_NAME,tenant_CLIENT_ID,tenant_CLIENT_SECRET,resource)
+            token = obtain_accesstoken(
+                tenant_TENANT_NAME,
+                tenant_CLIENT_ID,
+                tenant_CLIENT_SECRET,
+                resource)
             return token
 
     elif mode == 'standalone':
@@ -48,8 +59,10 @@ def getAuth(mode,localauth,tenant):
             TENANT_NAME = os.environ.get("TENANT_NAME")
             CLIENT_ID = os.environ.get("CLIENT_ID")
             CLIENT_SECRET = os.environ.get("CLIENT_SECRET")
-        if ((TENANT_NAME is None) or (CLIENT_ID is None) or (CLIENT_SECRET is None)):
+        if ((TENANT_NAME is None) or (CLIENT_ID is None)
+                or (CLIENT_SECRET is None)):
             raise Exception("One or more os.environ variables not set")
         else:
-            token = obtain_accesstoken(TENANT_NAME,CLIENT_ID,CLIENT_SECRET,resource)
-            return token
\ No newline at end of file
+            token = obtain_accesstoken(
+                TENANT_NAME, CLIENT_ID, CLIENT_SECRET, resource)
+            return token
diff --git a/src/IntuneCD/get_diff_output.py b/src/IntuneCD/get_diff_output.py
new file mode 100644
index 00000000..aee60c53
--- /dev/null
+++ b/src/IntuneCD/get_diff_output.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+"""
+This module is used to get diff output from the diff.
+"""
+
+import re
+
+
+def get_diff_output(data):
+    """
+    This function takes the data from the diff and returns a string with the differences.
+
+    :param data: The data from the diff
+    :return: A string with the differences
+    """
+    output = []
+
+    for key, value in data.items():
+        setting = re.search(
+            "\\[(.*)\\]", key).group(1).split("[")[-1]
+        new_val = value['new_value']
+        old_val = value['old_value']
+
+        output.append(f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
+
+    return output
diff --git a/src/IntuneCD/graph_batch.py b/src/IntuneCD/graph_batch.py
index ec8cdde4..b929e2cf 100644
--- a/src/IntuneCD/graph_batch.py
+++ b/src/IntuneCD/graph_batch.py
@@ -1,100 +1,79 @@
+#!/usr/bin/env python3
+
 """
 This module is used to batch requests to the Graph Batch endpoint. Two additional functions,
 get_object_assignment and get_object_details is used to retrieve the objects assignment and details from
 the batch request.
-
-Parameters batch_request
-----------
-data : list
-    List of object IDs to get data for
-url : str
-    MS graph endpoint for the object
-extra_url : str
-    Used if anything extra is needed for the url such as /assignments or ?$filter
-token : str
-    OAuth token used for authentication
-
-Parameters batch_assignment
-----------
-data : list
-    List of objects
-url : str
-    MS graph endpoint for the object
-extra_url : str
-    Used if anything extra is needed for the url such as /assignments or ?$filter
-token : str
-    OAuth token used for authentication
-app_protection : bool
-    By default False, set to true when getting assignments for APP to get the platform
-
-Parameters batch_intents
-----------
-data : list
-    List of objects
-token : str
-    OAuth token used for authentication
-
-Parameters get_object_assignments
-----------
-id : str
-    ID of the object to get assignments for
-responses : list
-    Responses from batch_assignment
-
-Parameters get_object_details
-----------
-id : str
-    ID of the object to get assignments for
-responses : list
-    Responses from batch request
 """
 
 import json
 from .graph_request import makeapirequestPost
 
+
 def batch_request(data, url, extra_url, token, method='GET') -> list:
+    """
+    Batch request to the Graph API.
+
+    :param data: List of object IDs to get data for
+    :param url: MS graph endpoint for the object
+    :param extra_url: Used if anything extra is needed for the url such as /assignments or ?$filter
+    :param token: OAuth token used for authentication
+    :param method: GET or POST
+    :return: List of responses from the batch request
+    """
 
     responses = []
     batch_id = 1
     batch_count = 20
-    ## Split objects into lists of 20
+    # Split objects into lists of 20
     batch_list = [data[i:i + batch_count]
                   for i in range(0, len(data), batch_count)]
 
-    ## Build a body for each ID in the list
+    # Build a body for each ID in the list
     for i in range(0, len(batch_list)):
         query_data = {'requests': []}
         for id in batch_list[i]:
             body = {
                 'id': batch_id,
                 'method': method,
-                'url': url+id+extra_url
+                'url': url + id + extra_url
             }
 
             batch_id += 1
             query_data['requests'].append(body)
 
-        ## POST to the graph batch endpoint
+        # 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_data = sorted(
             request['responses'], key=lambda item: item.get("id"))
 
-        ## Append each succcessful request to responses list
+        # Append each successful request to responses list
         for resp in request_data:
             if resp['status'] == 200:
                 responses.append(resp['body'])
 
     return responses
 
+
 def batch_assignment(data, url, extra_url, token, app_protection=False) -> list:
+    """
+    Batch request to the Graph API.
+
+    :param data: List of objects
+    :param url: MS graph endpoint for the object
+    :param extra_url: Used if anything extra is needed for the url such as /assignments or ?$filter
+    :param token: OAuth token used for authentication
+    :param app_protection: By default False, set to true when getting assignments for APP to get the platform
+    :return: List of responses from the batch request
+    """
 
     data_ids = []
     group_ids = []
     filter_ids = []
 
-    ## If getting App Protection Assignments, get the platform
+    # 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":
@@ -106,11 +85,11 @@ def batch_assignment(data, url, extra_url, token, app_protection=False) -> list:
             else:
                 data_ids.append(
                     f"{str(id['@odata.type']).split('.')[2]}s/{id['id']}")
-    ## Else, just add the objects ID to the list
+    # Else, just add the objects ID to the list
     else:
         for id in data['value']:
             data_ids.append(id['id'])
-    ## If we have any IDs, batch request the assignments
+    # If we have any IDs, batch request the assignments
     if data_ids:
         responses = batch_request(data_ids, url, extra_url, token)
         if responses:
@@ -119,12 +98,13 @@ def batch_assignment(data, url, extra_url, token, app_protection=False) -> list:
                          for keys, val in val.items() if 'groupId' in keys]
             filter_ids = [val for list in responses for val in list['value']
                           for keys, val in val.items() if 'target' in keys
-                          for keys, val in val.items() if 'deviceAndAppManagementAssignmentFilterId' in keys if val != None]
+                          for keys, val in val.items() if 'deviceAndAppManagementAssignmentFilterId' in keys if
+                          val is not None]
 
-        ## Batch get name of the groups
+        # Batch get name of the groups
         if group_ids:
             group_responses = batch_request(
-                group_ids, f'groups/', '?$select=displayName,id', token)
+                group_ids, 'groups/', '?$select=displayName,id', token)
             for value in responses:
                 if value['value']:
                     for val in value['value']:
@@ -133,10 +113,10 @@ def batch_assignment(data, url, extra_url, token, app_protection=False) -> list:
                                 if id['id'] == val['target']['groupId']:
                                     val['target']['groupName'] = id['displayName']
 
-        ## Batch get name of the Filters
+        # Batch get name of the Filters
         if filter_ids:
             filter_responses = batch_request(
-                filter_ids, f'deviceManagement/assignmentFilters/', '?$select=displayName', token)
+                filter_ids, 'deviceManagement/assignmentFilters/', '?$select=displayName', token)
             for value in responses:
                 if value['value']:
                     for val in value['value']:
@@ -147,7 +127,16 @@ def batch_assignment(data, url, extra_url, token, app_protection=False) -> list:
 
         return responses
 
+
 def batch_intents(data, token) -> dict:
+    """
+    Batch request to the Graph API.
+
+    :param data: List of objects
+    :param token: OAuth token used for authentication
+    :return: List of responses from the batch request
+    """
+
     base_url = 'deviceManagement'
     template_ids = []
     settings_id = []
@@ -155,17 +144,17 @@ def batch_intents(data, token) -> dict:
     settings_responses = []
     intent_values = {'value': []}
 
-    ## Get each template ID
+    # Get each template ID
     filtered_data = [val for list in data['value']
                      for key, val in list.items() if 'templateId' in key]
     template_ids = list(dict.fromkeys(filtered_data))
 
-    ## Batch get all categories from templates
+    # Batch get all categories from templates
     if template_ids:
         categories_responses = batch_request(
             template_ids, f'{base_url}/templates/', '/categories', token)
 
-    ## Build ID for requesting settings for each Intent
+    # 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'] in list['@odata.context']
@@ -173,12 +162,12 @@ def batch_intents(data, token) -> dict:
             for setting_id in settings_ids:
                 settings_id.append(f"{intent['id']}/categories/{setting_id}")
 
-    ## Batch get all settings for all Intents
+    # Batch get all settings for all Intents
     if settings_id:
         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 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']
@@ -194,7 +183,16 @@ def batch_intents(data, token) -> dict:
 
     return intent_values
 
+
 def get_object_assignment(id, responses) -> list:
+    """
+    Get the object assignment for the object ID.
+
+    :param id: Id of the object to get the assignment for
+    :param responses: List of responses from the batch request
+    :return: List of assignments for the object
+    """
+
     remove_keys = {'id', 'groupId', 'sourceId'}
     assignments_list = [
         val for list in responses if id in list['@odata.context'] for val in list['value']]
@@ -202,8 +200,18 @@ def get_object_assignment(id, responses) -> list:
         for k in remove_keys:
             value.pop(k, None)
             value['target'].pop(k, None)
+
     return assignments_list
 
+
 def get_object_details(id, responses) -> list:
+    """
+    Get the object details for the object ID.
+
+    :param id: Id of the object to get the details for
+    :param responses: List of responses from the batch request
+    :return: List of details for the object
+    """
+
     details = [val for list in responses if id in list['@odata.context'] for val in list['value']]
-    return details
\ No newline at end of file
+    return details
diff --git a/src/IntuneCD/graph_request.py b/src/IntuneCD/graph_request.py
index 84011e96..b5ffd726 100644
--- a/src/IntuneCD/graph_request.py
+++ b/src/IntuneCD/graph_request.py
@@ -1,67 +1,104 @@
-import requests
+#!/usr/bin/env python3
+
+"""
+This module contains the functions to make API requests to the Microsoft Graph API.
+"""
+
 import json
 import time
+import requests
+
+
+def makeapirequest(endpoint, token, q_param=None):
+    """
+    This function makes a GET request to the Microsoft Graph API.
 
-def makeapirequest(endpoint,token,q_param=None):
-
-    headers = {'Content-Type':'application/json', \
-    'Authorization':'Bearer {0}'.format(token['accessToken'])}
-    
-    if q_param != None:
-        response = requests.get(endpoint,headers=headers,params=q_param)
-        if response.status_code == 504 or response.status_code == 502:
-            print('Ran into issues with Graph request, waiting 5 seconds and trying again...')
-            time.sleep(5)
-            response = requests.get(endpoint,headers=headers)
+    :param endpoint: The endpoint to make the request to.
+    :param token: The token to use for authenticating the request.
+    :param q_param: The query parameters to use for the request.
+    :return: The response from the request.
+    """
+
+    headers = {'Content-Type': 'application/json',
+               'Authorization': 'Bearer {0}'.format(token['accessToken'])}
+
+    if q_param is not None:
+        response = requests.get(endpoint, headers=headers, params=q_param)
+        if response.status_code == 504 or response.status_code == 502 or response.status_code == 503:
+            print('Ran into issues with Graph request, waiting 10 seconds and trying again...')
+            time.sleep(10)
+            response = requests.get(endpoint, headers=headers)
     else:
-        response = requests.get(endpoint,headers=headers)
+        response = requests.get(endpoint, headers=headers)
         if response.status_code == 504 or response.status_code == 502 or response.status_code == 503:
             print('Ran into issues with Graph request, waiting 10 seconds and trying again...')
             time.sleep(10)
-            response = requests.get(endpoint,headers=headers)
+            response = requests.get(endpoint, headers=headers)
     if response.status_code == 200:
         json_data = json.loads(response.text)
 
         if '@odata.nextLink' in json_data.keys():
-            record = makeapirequest(json_data['@odata.nextLink'],token)
+            record = makeapirequest(json_data['@odata.nextLink'], token)
             entries = len(record['value'])
             count = 0
             while count < entries:
                 json_data['value'].append(record['value'][count])
                 count += 1
-        return(json_data)
+
+        return (json_data)
+
     elif response.status_code == 404:
         print("Resource not found in Microsoft Graph: " + endpoint)
-    elif (("assignmentFilters" in endpoint) and ("FeatureNotEnabled" in response.text)):
+    elif ("assignmentFilters" in endpoint) and ("FeatureNotEnabled" in response.text):
         print("Assignment filters not enabled in tenant, skipping")
     else:
-        raise Exception('Request failed with ',response.status_code,' - ',
-            response.text)
+        raise Exception('Request failed with ', response.status_code, ' - ',
+                        response.text)
+
 
-def makeapirequestPatch(patchEndpoint,token,q_param=None,jdata=None,status_code=200):
+def makeapirequestPatch(patchEndpoint, token, q_param=None, jdata=None, status_code=200):
+    """
+    This function makes a PATCH request to the Microsoft Graph API.
 
-    headers = {'Content-Type':'application/json', \
-    'Authorization':'Bearer {0}'.format(token['accessToken'])}
-    
-    if q_param != None:
-        response = requests.patch(patchEndpoint,headers=headers,params=q_param,data=jdata)
+    :param patchEndpoint: The endpoint to make the request to.
+    :param token: The token to use for authenticating the request.
+    :param q_param: The query parameters to use for the request.
+    :param jdata: The JSON data to use for the request.
+    :param status_code: The status code to expect from the request.
+    """
+
+    headers = {'Content-Type': 'application/json',
+               'Authorization': 'Bearer {0}'.format(token['accessToken'])}
+
+    if q_param is not None:
+        response = requests.patch(patchEndpoint, headers=headers, params=q_param, data=jdata)
     else:
-        response = requests.patch(patchEndpoint,headers=headers,data=jdata)
+        response = requests.patch(patchEndpoint, headers=headers, data=jdata)
     if response.status_code == status_code:
         pass
     else:
-        raise Exception('Request failed with ',response.status_code,' - ',
-            response.text)
+        raise Exception('Request failed with ', response.status_code, ' - ',
+                        response.text)
+
 
-def makeapirequestPost(patchEndpoint,token,q_param=None,jdata=None,status_code=200):
+def makeapirequestPost(patchEndpoint, token, q_param=None, jdata=None, status_code=200):
+    """
+    This function makes a POST request to the Microsoft Graph API.
 
-    headers = {'Content-Type':'application/json', \
-    'Authorization':'Bearer {0}'.format(token['accessToken'])}
-    
-    if q_param != None:
-        response = requests.post(patchEndpoint,headers=headers,params=q_param,data=jdata)
+    :param patchEndpoint: The endpoint to make the request to.
+    :param token: The token to use for authenticating the request.
+    :param q_param: The query parameters to use for the request.
+    :param jdata: The JSON data to use for the request.
+    :param status_code: The status code to expect from the request.
+    """
+
+    headers = {'Content-Type': 'application/json',
+               'Authorization': 'Bearer {0}'.format(token['accessToken'])}
+
+    if q_param is not None:
+        response = requests.post(patchEndpoint, headers=headers, params=q_param, data=jdata)
     else:
-        response = requests.post(patchEndpoint,headers=headers,data=jdata)
+        response = requests.post(patchEndpoint, headers=headers, data=jdata)
     if response.status_code == status_code:
         if response.text:
             json_data = json.loads(response.text)
@@ -69,20 +106,30 @@ def makeapirequestPost(patchEndpoint,token,q_param=None,jdata=None,status_code=2
         else:
             pass
     else:
-        raise Exception('Request failed with ',response.status_code,' - ',
-            response.text)
+        raise Exception('Request failed with ', response.status_code, ' - ',
+                        response.text)
+
+
+def makeapirequestPut(patchEndpoint, token, q_param=None, jdata=None, status_code=200):
+    """
+    This function makes a PUT request to the Microsoft Graph API.
+
+    :param patchEndpoint: The endpoint to make the request to.
+    :param token: The token to use for authenticating the request.
+    :param q_param: The query parameters to use for the request.
+    :param jdata: The JSON data to use for the request.
+    :param status_code: The status code to expect from the request.
+    """
 
-def makeapirequestPut(patchEndpoint,token,q_param=None,jdata=None,status_code=200):
+    headers = {'Content-Type': 'application/json',
+               'Authorization': 'Bearer {0}'.format(token['accessToken'])}
 
-    headers = {'Content-Type':'application/json', \
-    'Authorization':'Bearer {0}'.format(token['accessToken'])}
-    
-    if q_param != None:
-        response = requests.put(patchEndpoint,headers=headers,params=q_param,data=jdata)
+    if q_param is not None:
+        response = requests.put(patchEndpoint, headers=headers, params=q_param, data=jdata)
     else:
-        response = requests.put(patchEndpoint,headers=headers,data=jdata)
+        response = requests.put(patchEndpoint, headers=headers, data=jdata)
     if response.status_code == status_code:
         pass
     else:
-        raise Exception('Request failed with ',response.status_code,' - ',
-            response.text)
\ No newline at end of file
+        raise Exception('Request failed with ', response.status_code, ' - ',
+                        response.text)
diff --git a/src/IntuneCD/load_file.py b/src/IntuneCD/load_file.py
new file mode 100644
index 00000000..16cb2e80
--- /dev/null
+++ b/src/IntuneCD/load_file.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+
+"""
+This module is used to load a file into a dictionary.
+"""
+
+import json
+import yaml
+
+
+def load_file(filename, file):
+    """
+    This function loads a JSON or YAML file to a dictionary.
+    :param filename: The name of the file.
+    :param file: The file to load.
+    :return: The dictionary.
+    """
+
+    if filename.endswith(".yaml"):
+        data = json.dumps(yaml.safe_load(file))
+        repo_data = json.loads(data)
+
+    elif filename.endswith(".json"):
+        repo_data = json.load(file)
+
+    else:
+        raise ValueError(f"{filename} is not a valid file type.")
+
+    return repo_data
diff --git a/src/IntuneCD/remove_keys.py b/src/IntuneCD/remove_keys.py
new file mode 100644
index 00000000..3b81fe72
--- /dev/null
+++ b/src/IntuneCD/remove_keys.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+
+"""
+This module is used to remove keys from the data.
+"""
+
+
+def remove_keys(data):
+    """
+    This function removes keys from the data.
+    :param data: The data to remove keys from.
+    :return: The data with removed keys.
+    """
+
+    keys = {
+        'id',
+        'version',
+        'topicIdentifier',
+        'certificate',
+        'createdDateTime',
+        'lastModifiedDateTime',
+        'isDefault'
+        'deployedAppCount',
+        'isAssigned',
+        '@odata.context',
+        'scheduledActionConfigurations@odata.context',
+        'scheduledActionsForRule@odata.context',
+        'sourceId',
+        'supportsScopeTags',
+        'companyCodes',
+        'isGlobalScript',
+        'highestAvailableVersion',
+        'token',
+        'lastSyncDateTime',
+        'isReadOnly',
+        'secretReferenceValueId',
+        'isEncrypted'}
+    for k in keys:
+        data.pop(k, None)
+
+    return data
diff --git a/src/IntuneCD/run_backup.py b/src/IntuneCD/run_backup.py
index 57844cef..aaefa9e7 100644
--- a/src/IntuneCD/run_backup.py
+++ b/src/IntuneCD/run_backup.py
@@ -1,145 +1,235 @@
 #!/usr/bin/env python3
 
+"""
+This module contains the functions to run the backup.
+"""
+
 import os
+import sys
+import base64
+import argparse
 
+from io import StringIO
 from .get_authparams import getAuth
-from optparse import OptionParser
+from .update_frontend import update_frontend
 
 REPO_DIR = os.environ.get("REPO_DIR")
 
-def start():
 
-    parser = OptionParser(description="Save backup of Intune configurations")
-    parser.add_option(
-        "-o", "--output", 
+def start():
+    parser = argparse.ArgumentParser(
+        description="Save backup of Intune configurations")
+    parser.add_argument(
+        "-o",
+        "--output",
         help='The format backups will be saved as, valid options are json or yaml. Default is json',
         type=str,
-        default = "json",
+        default="json",
     )
-    parser.add_option(
-        "-p", "--path", 
+    parser.add_argument(
+        "-p",
+        "--path",
         help='The path to which the configurations will be saved. Default value is $(Build.SourcesDirectory)',
         type=str,
-        default = REPO_DIR,
+        default=REPO_DIR,
     )
-    parser.add_option(
-        "-m", "--mode",
-        help = ("The mode in which the script is run, 0 = devtoprod (backup from dev -> update to prod) "
-                "uses os.environ DEV_TENANT_NAME, DEV_CLIENT_ID, DEV_CLIENT_SECRET, "
-                "1 = standalone (backup from prod) uses os.environ TENANT_NAME, CLIENT_ID, CLIENT_SECRET"),
+    parser.add_argument(
+        "-m",
+        "--mode",
+        help=(
+            "The mode in which the script is run, 0 = devtoprod (backup from dev -> update to prod) "
+            "uses os.environ DEV_TENANT_NAME, DEV_CLIENT_ID, DEV_CLIENT_SECRET, "
+            "1 = standalone (backup from prod) uses os.environ TENANT_NAME, CLIENT_ID, CLIENT_SECRET"),
         type=int,
-        default = 0
-    )
-    parser.add_option(
-        "-a", "--localauth",
-        help=("When this paramater is set, provide a path to a local dict file containing the following keys: "
-                "params:TENANT_NAME, CLIENT_ID, CLIENT_SECRET when run in standalone mode and "
-                "params:DEV_TENANT_NAME, DEV_CLIENT_ID, DEV_CLIENT_SECRET when run in devtoprod"),
-        type=str
-    )
-    parser.add_option(
-        "-e", "--exclude",
-        help = "List of objects to exclude from the backup, separated by commas. Currently supported objects are: assignments",
-        type = str
-    )
-
-    (opts, _) = parser.parse_args()
+        default=0)
+    parser.add_argument(
+        "-a",
+        "--localauth",
+        help=(
+            "When this paramater is set, provide a path to a local dict file containing the following keys: "
+            "params:TENANT_NAME, CLIENT_ID, CLIENT_SECRET when run in standalone mode and "
+            "params:DEV_TENANT_NAME, DEV_CLIENT_ID, DEV_CLIENT_SECRET when run in devtoprod"),
+        type=str)
+    parser.add_argument(
+        "-e",
+        "--exclude",
+        help="List of objects to exclude from the backup, separated by space.",
+        choices=[
+            "assignments",
+            "AppConfigurations",
+            "AppProtection",
+            "APNs",
+            "VPP",
+            "Applications",
+            "Compliance",
+            "NotificationTemplate",
+            "Profiles",
+            "GPOConfigurations",
+            "AppleEnrollmentProfile",
+            "WindowsEnrollmentProfile",
+            "Filters",
+            "ManagedGooglePlay",
+            "Intents",
+            "CompliancePartner",
+            "ManagementPartner",
+            "RemoteAssistancePartner",
+            "ProactiveRemediation",
+            "PowershellScripts",
+            "ShellScripts",
+            "ConfigurationPolicies"],
+        nargs='+')
+    parser.add_argument(
+        "-f",
+        "--frontend",
+        help="Set the frontend URL to update with configuration count and backup stream",
+        type=str)
+
+    args = parser.parse_args()
 
     def devtoprod():
         return "devtoprod"
- 
+
     def standalone():
         return "standalone"
- 
+
     switcher = {
         0: devtoprod,
         1: standalone
     }
- 
+
     def selected_mode(argument):
         func = switcher.get(argument, "nothing")
         return func()
 
-    token = getAuth(selected_mode(opts.mode),opts.localauth,tenant="DEV")
+    token = getAuth(selected_mode(args.mode), args.localauth, tenant="DEV")
 
-    def run_backup(path,output,exclude,token):
+    def run_backup(path, output, exclude, token):
 
-        from .backup_appConfiguration import savebackup
-        savebackup(path,output,exclude,token)
+        config_count = 0
 
-        from .backup_AppProtection import savebackup
-        savebackup(path,output,exclude,token)
+        if "AppConfigurations" not in exclude:
+            from .backup_appConfiguration import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_apns import savebackup
-        savebackup(path,output,token)
+        if "AppProtection" not in exclude:
+            from .backup_AppProtection import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_vppTokens import savebackup
-        savebackup(path,output,token)
+        if "APNs" not in exclude:
+            from .backup_apns import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_applications import savebackup
-        savebackup(path,output,exclude,token)
+        if "VPP" not in exclude:
+            from .backup_vppTokens import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_compliance import savebackup
-        savebackup(path,output,exclude,token)
+        if "Applications" not in exclude:
+            from .backup_applications import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_notificationTemplate import savebackup
-        savebackup(path,output,token)
+        if "Compliance" not in exclude:
+            from .backup_compliance import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_profiles import savebackup
-        savebackup(path,output,exclude,token)
+        if "NotificationTemplate" not in exclude:
+            from .backup_notificationTemplate import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_groupPolicyConfiguration import savebackup
-        savebackup(path,output,exclude,token)
+        if "Profiles" not in exclude:
+            from .backup_profiles import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_appleEnrollmentProfile import savebackup
-        savebackup(path,output,token)
+        if "GPOConfigurations" not in exclude:
+            from .backup_groupPolicyConfiguration import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_windowsEnrollmentProfile import savebackup
-        savebackup(path,output,exclude,token)
+        if "AppleEnrollmentProfile" not in exclude:
+            from .backup_appleEnrollmentProfile import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_assignmentFilters import savebackup
-        savebackup(path,output,token)
+        if "WindowsEnrollmentProfile" not in exclude:
+            from .backup_windowsEnrollmentProfile import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_managedGPlay import savebackup
-        savebackup(path,output,token)
+        if "Filters" not in exclude:
+            from .backup_assignmentFilters import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_managementIntents import savebackup
-        savebackup(path,output,exclude,token)
+        if "ManagedGooglePlay" not in exclude:
+            from .backup_managedGPlay import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_compliancePartner import savebackup
-        savebackup(path,output,token)
+        if "Intents" not in exclude:
+            from .backup_managementIntents import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_managementPartner import savebackup
-        savebackup(path,output,token)
+        if "CompliancePartner" not in exclude:
+            from .backup_compliancePartner import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_remoteAssistancePartner import savebackup
-        savebackup(path,output,token)
+        if "ManagementPartner" not in exclude:
+            from .backup_managementPartner import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_proactiveRemediation import savebackup
-        savebackup(path,output,exclude,token)
+        if "RemoteAssistancePartner" not in exclude:
+            from .backup_remoteAssistancePartner import savebackup
+            config_count += savebackup(path, output, token)
 
-        from .backup_powershellScripts import savebackup
-        savebackup(path,output,exclude,token)
+        if "ProactiveRemediation" not in exclude:
+            from .backup_proactiveRemediation import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_shellScripts import savebackup
-        savebackup(path,output,exclude,token)
+        if "PowershellScripts" not in exclude:
+            from .backup_powershellScripts import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-        from .backup_configurationPolicies import savebackup
-        savebackup(path,output,exclude,token)
+        if "ShellScripts" not in exclude:
+            from .backup_shellScripts import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
+        if "ConfigurationPolicies" not in exclude:
+            from .backup_configurationPolicies import savebackup
+            config_count += savebackup(path, output, exclude, token)
 
-    if opts.output == 'json' or opts.output == 'yaml':
+        return config_count
+
+    if args.output == 'json' or args.output == 'yaml':
         if token is None:
-            raise Exception("Token is empty, please check os.environ variables")
+            raise Exception(
+                "Token is empty, please check os.environ variables")
+
+        if args.exclude:
+            exclude = args.exclude
         else:
-            if opts.exclude:
-                exclude = opts.exclude.split(",")
-            else:
-                exclude = []
-            run_backup(opts.path,opts.output,exclude,token)
+            exclude = []
+
+        if args.frontend:
+
+            old_stdout = sys.stdout
+            sys.stdout = feedstdout = StringIO()
+            count = run_backup(args.path, args.output, exclude, token)
+            sys.stdout = old_stdout
+            feed_bytes = feedstdout.getvalue().encode("utf-8")
+            out = base64.b64encode(feed_bytes).decode("utf-8")
+
+            body = {
+                "type": "config_count",
+                "config_count": count
+            }
+            update_frontend(f'{args.frontend}/api/overview/summary', body)
+
+            body = {
+                "type": "backup",
+                "feed": out
+            }
+            update_frontend(f'{args.frontend}/api/feed/update', body)
+
+        else:
+            run_backup(args.path, args.output, exclude, token)
 
     else:
-        print('Please enter a valid output format, json or yaml') 
+        print('Please enter a valid output format, json or yaml')
+
 
 if __name__ == "__main__":
-    start()
\ No newline at end of file
+    start()
diff --git a/src/IntuneCD/run_documentation.py b/src/IntuneCD/run_documentation.py
index 87e720d9..384e3c8d 100644
--- a/src/IntuneCD/run_documentation.py
+++ b/src/IntuneCD/run_documentation.py
@@ -1,48 +1,61 @@
 #!/usr/bin/env python3
 
+"""
+This module contains the functions to run the documentation.
+"""
+
 import os
+import json
+import argparse
+
 from datetime import datetime
-from .documentation_functions import document_configs,document_management_intents,md_file,get_md_files
 from markdown_toclify import markdown_toclify
-from optparse import OptionParser
+from .documentation_functions import document_configs, document_management_intents, md_file, get_md_files
 
 REPO_DIR = os.environ.get("REPO_DIR")
 
-def start ():
 
-    parser = OptionParser(description="Create markdown document from backup files")
-    parser.add_option(
+def start():
+    parser = argparse.ArgumentParser(description="Create markdown document from backup files")
+    parser.add_argument(
         "-p", "--path",
-        help = 'Path to where the backup is saved, default is REPO_DIR',
-        default = REPO_DIR
+        help='Path to where the backup is saved, default is REPO_DIR',
+        default=REPO_DIR
     )
-    parser.add_option(
+    parser.add_argument(
         "-o", "--outpath",
-        help = 'The path to save the document, default is REPO_DIR/README.md',
-        default = f'{REPO_DIR}/README.md'
+        help='The path to save the document, default is REPO_DIR/README.md',
+        default=f'{REPO_DIR}/README.md'
     )
-    parser.add_option(
+    parser.add_argument(
         '-t', '--tenantname',
-        help = 'Name of the tenant'
+        help='Name of the tenant'
     )
-    parser.add_option(
+    parser.add_argument(
         '-i', '--intro',
-        help = 'Introduction that will be added to the top of the document',
-        default = 'This document contains documentation of all configurations exported by the IntuneCD tool'
+        help='Introduction that will be added to the top of the document',
+        default='This document contains documentation of all configurations exported by the IntuneCD tool'
+    )
+    parser.add_argument(
+        '-j', '--jsondata',
+        help='Lets you configure line 1-4 using a JSON string: "{\\"title\\": \\"demo\\", \\"intro\\": \\"demo\\", '
+             '\\"tenant\\": \\"demo\\", \\"updated\\": \\"demo\\"}" '
     )
-    parser.add_option(
+    parser.add_argument(
         '-m', '--maxlength',
-        help = 'Maximum length of the configuration value, values with a higher count will be displayed with "Value too long to display"',
-        type = int
+        help='Maximum length of the configuration value, values with a higher count will be displayed with "Value '
+             'too long to display"',
+        type=int
     )
-    parser.add_option(
+    parser.add_argument(
         '-s', '--split',
-        help = 'Split the documentation into multiple files and create index.md in the configpath directory with a list of all files',
+        help='Split the documentation into multiple files and create index.md in the configpath directory with a '
+             'list of all files',
     )
 
-    (opts, _) = parser.parse_args()
+    args = parser.parse_args()
 
-    def run_documentation(configpath,outpath,tenantname,maxlength,split):
+    def run_documentation(configpath, outpath, tenantname, jsondata,  maxlength, split):
 
         now = datetime.now()
         current_date = now.strftime("%d/%m/%Y %H:%M:%S")
@@ -53,102 +66,132 @@ def run_documentation(configpath,outpath,tenantname,maxlength,split):
             md_file(outpath)
 
         # Document App Configuration
-        document_configs(f'{configpath}/App Configuration',outpath,'App Configuration',maxlength,split)
+        document_configs(f'{configpath}/App Configuration', outpath, 'App Configuration', maxlength, split)
 
         # Document App Protection
-        document_configs(f'{configpath}/App Protection',outpath,'App Protection',maxlength,split)
+        document_configs(f'{configpath}/App Protection', outpath, 'App Protection', maxlength, split)
 
         # Document Apple Push Notification
-        document_configs(f'{configpath}/Apple Push Notification',outpath,'Apple Push Notification',maxlength,split)
+        document_configs(f'{configpath}/Apple Push Notification', outpath, 'Apple Push Notification', maxlength, split)
 
         # Document Apple VPP Tokens
-        document_configs(f'{configpath}/Apple VPP Tokens',outpath,'Apple VPP Tokens',maxlength,split)
+        document_configs(f'{configpath}/Apple VPP Tokens', outpath, 'Apple VPP Tokens', maxlength, split)
 
         # Document iOS Applications
-        document_configs(f'{configpath}/Applications/iOS',outpath,'iOS Applications',maxlength,split)
+        document_configs(f'{configpath}/Applications/iOS', outpath, 'iOS Applications', maxlength, split)
 
         # Document macOS Applications
-        document_configs(f'{configpath}/Applications/macOS',outpath,'macOS Applications',maxlength,split)
+        document_configs(f'{configpath}/Applications/macOS', outpath, 'macOS Applications', maxlength, split)
 
         # Document Android Applications
-        document_configs(f'{configpath}/Applications/Android',outpath,'Android Applications',maxlength,split)
+        document_configs(f'{configpath}/Applications/Android', outpath, 'Android Applications', maxlength, split)
 
         # Document Windows Applications
-        document_configs(f'{configpath}/Applications/Windows',outpath,'Windows Applications',maxlength,split)
+        document_configs(f'{configpath}/Applications/Windows', outpath, 'Windows Applications', maxlength, split)
 
         # Document Web Apps
-        document_configs(f'{configpath}/Applications/Web App',outpath,'Web Applications',maxlength,split)
+        document_configs(f'{configpath}/Applications/Web App', outpath, 'Web Applications', maxlength, split)
 
         # Document Office Suite apps
-        document_configs(f'{configpath}/Applications/Office Suite',outpath,'Office Suite Applications',maxlength,split)
+        document_configs(f'{configpath}/Applications/Office Suite', outpath, 'Office Suite Applications', maxlength,
+                         split)
 
         # Document compliance
-        document_configs(f'{configpath}/Compliance Policies/Policies',outpath,'Compliance Policies',maxlength,split)
+        document_configs(f'{configpath}/Compliance Policies/Policies', outpath, 'Compliance Policies', maxlength, split)
 
         # Message Templates
-        document_configs(f'{configpath}/Compliance Policies/Message Templates',outpath,'Message Templates',maxlength,split)
+        document_configs(f'{configpath}/Compliance Policies/Message Templates', outpath, 'Message Templates', maxlength,
+                         split)
 
         # Document profiles
-        document_configs(f'{configpath}/Device Configurations',outpath,'Configuration Profiles',maxlength,split)
+        document_configs(f'{configpath}/Device Configurations', outpath, 'Configuration Profiles', maxlength, split)
 
         # Document Group Policy Configurations
-        document_configs(f'{configpath}/Group Policy Configurations',outpath,'Group Policy Configurations',maxlength,split)
+        document_configs(f'{configpath}/Group Policy Configurations', outpath, 'Group Policy Configurations', maxlength,
+                         split)
 
         # Document Apple Enrollment Profiles
-        document_configs(f'{configpath}/Enrollment Profiles/Apple',outpath,'Apple Enrollment Profiles',maxlength,split)
+        document_configs(f'{configpath}/Enrollment Profiles/Apple', outpath, 'Apple Enrollment Profiles', maxlength,
+                         split)
 
         # Document Windows Enrollment Profiles
-        document_configs(f'{configpath}/Enrollment Profiles/Windows',outpath,'Windows Enrollment Profiles',maxlength,split)
+        document_configs(f'{configpath}/Enrollment Profiles/Windows', outpath, 'Windows Enrollment Profiles', maxlength,
+                         split)
 
         # Document filters
-        document_configs(f'{configpath}/Filters',outpath,'Filters',maxlength,split)
+        document_configs(f'{configpath}/Filters', outpath, 'Filters', maxlength, split)
 
         # Managed Google Play
-        document_configs(f'{configpath}/Managed Google Play',outpath,'Managed Google Play',maxlength,split)
+        document_configs(f'{configpath}/Managed Google Play', outpath, 'Managed Google Play', maxlength, split)
 
         # Document Intents
-        document_management_intents(f'{configpath}/Management Intents/',outpath,'Management Intents',split)
+        document_management_intents(f'{configpath}/Management Intents/', outpath, 'Management Intents', split)
 
-        #Document Partner Connections
-        document_configs(f'{configpath}/Partner Connections/',outpath,'Partner Connections',maxlength,split)
+        # Document Partner Connections
+        document_configs(f'{configpath}/Partner Connections/', outpath, 'Partner Connections', maxlength, split)
 
         # Document Proactive Remediations
-        document_configs(f'{configpath}/Proactive Remediations',outpath,'Proactive Remediations',maxlength,split)
+        document_configs(f'{configpath}/Proactive Remediations', outpath, 'Proactive Remediations', maxlength, split)
 
         # Document Shell Scripts
-        document_configs(f'{configpath}/Scripts/Shell',outpath,'Shell Scripts',maxlength,split)
+        document_configs(f'{configpath}/Scripts/Shell', outpath, 'Shell Scripts', maxlength, split)
 
         # Document Powershell Scripts
-        document_configs(f'{configpath}/Scripts/Powershell',outpath,'Powershell Scripts',maxlength,split)
+        document_configs(f'{configpath}/Scripts/Powershell', outpath, 'Powershell Scripts', maxlength, split)
 
         # Document Settings Catalog
-        document_configs(f'{configpath}/Settings Catalog',outpath,'Settings Catalog',maxlength,split)
+        document_configs(f'{configpath}/Settings Catalog', outpath, 'Settings Catalog', maxlength, split)
+
+        if jsondata:
+            json_dict = json.loads(jsondata)
+            if "title" in json_dict:
+                title = json_dict["title"]
+            else:
+                title = "MEM Documentation"
+            if "intro" in json_dict:
+                intro = json_dict["intro"]
+            else:
+                intro = args.intro
+            if "tenant" in json_dict:
+                tenant = json_dict["tenant"]
+            else:
+                tenant = f'**Tenant:** {tenantname}'
+            if "updated" in json_dict:
+                updated = json_dict["updated"]
+            else:
+                updated = f'**Document updated on:**'
+        else:
+            title = "MEM Documentation"
+            intro = args.intro
+            tenant = f'**Tenant:** {tenantname}'
+            updated = f'**Document updated on:**'
 
-        if split == True:
+        if split:
             files = get_md_files()
             index_md = f'{configpath}/index.md'
             md_file(index_md)
 
             with open(index_md, 'w') as doc:
-                l1='# MEM Documentation \n\n'
-                l2=f'{opts.intro} \n\n'
-                l3=f'**Tenant:** {tenantname} \n\n'
-                l4=f'**Document updated on:** {current_date} \n\n'
-                l5='## File index \n\n'
-                doc.writelines([l1,l2,l3,l4,l5])
+                l1 = f'# {title} \n\n'
+                l2 = f'{intro} \n\n'
+                l3 = f'{tenant} \n\n'
+                l4 = f'{updated} {current_date} \n\n'
+                l5 = '## File index \n\n'
+                doc.writelines([l1, l2, l3, l4, l5])
                 for file in files:
-                    doc.writelines(['[',str(file).split('/')[-1],'](',str(file).replace(" ", "%20"),') \n\n'])
+                    doc.writelines(['[', str(file).split('/')[-1], '](', str(file).replace(" ", "%20"), ') \n\n'])
 
         else:
-            document = markdown_toclify(input_file=outpath,back_to_top=True,exclude_h=[3])
+            document = markdown_toclify(input_file=outpath, back_to_top=True, exclude_h=[3])
             with open(outpath, 'w') as doc:
-                l1='# MEM Documentation \n\n'
-                l2=f'{opts.intro} \n\n'
-                l3=f'**Tenant:** {tenantname} \n\n'
-                l4=f'**Document updated on:** {current_date} \n\n'
-                doc.writelines([l1,l2,l3,l4,document])
+                l1 = f'# {title} \n\n'
+                l2 = f'{intro} \n\n'
+                l3 = f'{tenant} \n\n'
+                l4 = f'{updated} {current_date} \n\n'
+                doc.writelines([l1, l2, l3, l4, document])
+
+    run_documentation(args.path, args.outpath, args.tenantname, args.jsondata, args.maxlength, args.split)
 
-    run_documentation(opts.path,opts.outpath,opts.tenantname,opts.maxlength,opts.split)
 
 if __name__ == '__main__':
-    start()
\ No newline at end of file
+    start()
diff --git a/src/IntuneCD/run_update.py b/src/IntuneCD/run_update.py
index 5fd7c493..5d3b26d2 100644
--- a/src/IntuneCD/run_update.py
+++ b/src/IntuneCD/run_update.py
@@ -1,105 +1,186 @@
 #!/usr/bin/env python3
 
+"""
+This module contains the functions to run the update.
+"""
+
 import os
+import sys
+import base64
+import argparse
 
+from io import StringIO
 from .get_authparams import getAuth
-from optparse import OptionParser
+from .update_frontend import update_frontend
 
 REPO_DIR = os.environ.get("REPO_DIR")
 
-def start():
 
-    parser = OptionParser(description="Update Intune configurations with values from backup")
-    parser.add_option(
-        "-p", "--path", 
+def start():
+    parser = argparse.ArgumentParser(
+        description="Update Intune configurations with values from backup")
+    parser.add_argument(
+        "-p",
+        "--path",
         help='The path to which the configurations are saved. Default value is $(Build.SourcesDirectory)',
-        default = REPO_DIR,
+        default=REPO_DIR,
     )
-    parser.add_option(
-        "-m", "--mode",
-        help = ("The mode in which the script is run, 0 = devtoprod (backup from dev -> update to prod) "
-                "uses os.environ PROD_TENANT_NAME, PROD_CLIENT_ID, PROD_CLIENT_SECRET, "
-                "1 = standalone (backup from prod) uses os.environ TENANT_NAME, CLIENT_ID, CLIENT_SECRET"),
+    parser.add_argument(
+        "-m",
+        "--mode",
+        help=(
+            "The mode in which the script is run, 0 = devtoprod (backup from dev -> update to prod) "
+            "uses os.environ PROD_TENANT_NAME, PROD_CLIENT_ID, PROD_CLIENT_SECRET, "
+            "1 = standalone (backup from prod) uses os.environ TENANT_NAME, CLIENT_ID, CLIENT_SECRET"),
         type=int,
-        default = 0
-    )
-    parser.add_option(
-        "-a", "--localauth",
-        help=("When this paramater is set, provide a path to a local dict file containing the following keys: "
-               "params:TENANT_NAME, CLIENT_ID, CLIENT_SECRET when run in standalone mode and "
-               "params:PROD_TENANT_NAME, PROD_CLIENT_ID, PROD_CLIENT_SECRET when run in devtoprod"),
-        type=str
-    )
-    parser.add_option(
+        default=0)
+    parser.add_argument(
+        "-a",
+        "--localauth",
+        help=(
+            "When this paramater is set, provide a path to a local dict file containing the following keys: "
+            "params:TENANT_NAME, CLIENT_ID, CLIENT_SECRET when run in standalone mode and "
+            "params:PROD_TENANT_NAME, PROD_CLIENT_ID, PROD_CLIENT_SECRET when run in devtoprod"),
+        type=str)
+    parser.add_argument(
         "-u",
-        help = "When this parameter is set, assignments are updated for all configurations",
-        action = "store_true"
-    )
-
-    (opts, _) = parser.parse_args()
+        help="When this parameter is set, assignments are updated for all configurations",
+        action="store_true")
+    parser.add_argument(
+        "-f",
+        "--frontend",
+        help="Set the frontend URL to update with configuration count and backup stream",
+        type=str)
+    parser.add_argument(
+        "-e",
+        "--exclude",
+        help="List of objects to exclude from the backup, separated by space.",
+        choices=[
+            "AppConfigurations",
+            "AppProtection",
+            "Compliance",
+            "NotificationTemplate",
+            "Profiles",
+            "AppleEnrollmentProfile",
+            "WindowsEnrollmentProfile",
+            "Filters",
+            "Intents",
+            "ProactiveRemediation",
+            "PowershellScripts",
+            "ShellScripts",
+            "ConfigurationPolicies"],
+        nargs='+')
+
+    args = parser.parse_args()
 
     def devtoprod():
         return "devtoprod"
- 
+
     def standalone():
         return "standalone"
- 
+
     switcher = {
         0: devtoprod,
         1: standalone
     }
- 
+
     def selected_mode(argument):
         func = switcher.get(argument, "nothing")
         return func()
 
-    token = getAuth(selected_mode(opts.mode),opts.localauth,tenant="PROD")
+    token = getAuth(selected_mode(args.mode), args.localauth, tenant="PROD")
 
-    def run_update(path,token,assignment):
+    def run_update(path, token, assignment, exclude):
 
-        from .update_appConfiguration import update
-        update(path,token,assignment)
+        diff_count = 0
 
-        from .update_appProtection import update
-        update(path,token,assignment)
+        if "AppConfigurations" not in exclude:
+            from .update_appConfiguration import update
+            diff_count += update(path, token, assignment)
 
-        from .update_compliance import update
-        update(path,token,assignment)
+        if "AppProtection" not in exclude:
+            from .update_appProtection import update
+            diff_count += update(path, token, assignment)
 
-        from .update_notificationTemplate import update
-        update(path,token)
+        if "Compliance" not in exclude:
+            from .update_compliance import update
+            diff_count += update(path, token, assignment)
 
-        from .update_profiles import update
-        update(path,token,assignment)
+        if "NotificationTemplate" not in exclude:
+            from .update_notificationTemplate import update
+            diff_count += update(path, token)
 
-        from .update_appleEnrollmentProfile import update
-        update(path,token)
+        if "Profiles" not in exclude:
+            from .update_profiles import update
+            diff_count += update(path, token, assignment)
 
-        from .update_windowsEnrollmentProfile import update
-        update(path,token,assignment)
+        if "AppleEnrollmentProfile" not in exclude:
+            from .update_appleEnrollmentProfile import update
+            diff_count += update(path, token)
 
-        from .update_assignmentFilter import update
-        update(path,token)
+        if "WindowsEnrollmentProfile" not in exclude:
+            from .update_windowsEnrollmentProfile import update
+            diff_count += update(path, token, assignment)
 
-        from .update_managementIntents import update
-        update(path,token,assignment)
+        if "Filters" not in exclude:
+            from .update_assignmentFilter import update
+            diff_count += update(path, token)
 
-        from .update_proactiveRemediation import update
-        update(path,token,assignment)
+        if "Intents" not in exclude:
+            from .update_managementIntents import update
+            diff_count += update(path, token, assignment)
 
-        from .update_powershellScripts import update
-        update(path,token,assignment)
+        if "ProactiveRemediation" not in exclude:
+            from .update_proactiveRemediation import update
+            diff_count += update(path, token, assignment)
 
-        from .update_shellScripts import update
-        update(path,token,assignment)
+        if "PowershellScripts" not in exclude:
+            from .update_powershellScripts import update
+            diff_count += update(path, token, assignment)
 
-        from .update_configurationPolicies import update
-        update(path,token,assignment)
+        if "ShellScripts" not in exclude:
+            from .update_shellScripts import update
+            diff_count += update(path, token, assignment)
+
+        if "ConfigurationPolicies" not in exclude:
+            from .update_configurationPolicies import update
+            diff_count += update(path, token, assignment)
+
+        return diff_count
 
     if token is None:
         raise Exception("Token is empty, please check os.environ variables")
     else:
-        run_update(opts.path,token,opts.u)
+
+        if args.exclude:
+            exclude = args.exclude
+        else:
+            exclude = []
+
+        if args.frontend:
+
+            old_stdout = sys.stdout
+            sys.stdout = feedstdout = StringIO()
+            count = run_update(args.path, token, args.u, exclude)
+            sys.stdout = old_stdout
+            feed_bytes = feedstdout.getvalue().encode("utf-8")
+            out = base64.b64encode(feed_bytes).decode("utf-8")
+
+            body = {
+                "type": "diff_count",
+                "diff_count": count
+            }
+            update_frontend(f'{args.frontend}/api/overview/summary', body)
+
+            body = {
+                "type": "update",
+                "feed": out
+            }
+            update_frontend(f'{args.frontend}/api/feed/update', body)
+
+        else:
+            run_update(args.path, token, args.u, exclude)
+
 
 if __name__ == "__main__":
-    start()
\ No newline at end of file
+    start()
diff --git a/src/IntuneCD/save_output.py b/src/IntuneCD/save_output.py
new file mode 100644
index 00000000..3ad07c6a
--- /dev/null
+++ b/src/IntuneCD/save_output.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+
+"""
+This module is used to save the configuration to a file.
+"""
+
+import os
+import json
+import yaml
+
+
+def save_output(output, configpath, fname, data):
+    """
+    This function saves the configuration to a file in JSON or YAML format.
+
+    :param output: The format the configuration will be saved as
+    :param configpath: The path to save the configuration to
+    :param fname: The filename of the configuration
+    :param data: The configuration data
+    """
+
+    if not os.path.exists(configpath):
+        os.makedirs(configpath)
+
+    if output == 'yaml':
+        with open(configpath + fname + ".yaml", 'w') as yamlFile:
+            yaml.dump(data, yamlFile, sort_keys=False,
+                      default_flow_style=False)
+    elif output == 'json':
+        with open(configpath + fname + ".json", 'w') as jsonFile:
+            json.dump(data, jsonFile, indent=10)
+
+    else:
+        raise ValueError("Invalid output format")
diff --git a/src/IntuneCD/update_appConfiguration.py b/src/IntuneCD/update_appConfiguration.py
index 15b351d0..b70732bc 100644
--- a/src/IntuneCD/update_appConfiguration.py
+++ b/src/IntuneCD/update_appConfiguration.py
@@ -1,147 +1,163 @@
 #!/usr/bin/env python3
 
 """
-This module updates all App Configuration Polices in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all App Configuration Assignments in Intune.
 """
 
 import json
 import os
-import yaml
-import re
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from .graph_batch import batch_assignment, get_object_assignment
 from .update_assignment import update_assignment, post_assignment_update
-from deepdiff import DeepDiff
+from .remove_keys import remove_keys
+from .get_diff_output import get_diff_output
+from .check_file import check_file
+from .load_file import load_file
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations"
-app_endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations"
+APP_ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"
 
 
 def update(path, token, assignment=False):
-
-    ## Set App Configuration path
-    configpath = path+"/"+"App Configuration/"
-    ## If App Configuration path exists, continue
-    if os.path.exists(configpath) == True:
-
-        ## Get App Configurations
-        mem_data = makeapirequest(endpoint, token)
-        ## Get current assignments
+    """
+    This function updates all App Configuration Polices 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
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set App Configuration path
+    configpath = path + "/" + "App Configuration/"
+    # If App Configuration path exists, continue
+    if os.path.exists(configpath):
+
+        # Get App Configurations
+        mem_data = makeapirequest(ENDPOINT, token)
+        # Get current assignments
         mem_assignments = batch_assignment(
-            mem_data, 'deviceAppManagement/mobileAppConfigurations/', '/assignments', token)
+            mem_data,
+            'deviceAppManagement/mobileAppConfigurations/',
+            '/assignments',
+            token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
-                continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
+            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
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                        
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    ## If App Configuration exists, continue
-                    data = {'value': ''}
-                    if mem_data['value']:
-                        for val in mem_data['value']:
-                            if repo_data['@odata.type'] == val['@odata.type'] and \
-                               repo_data['displayName'] == val['displayName']:
-                                data['value'] = val
-
-                    if data['value']:
-                        print("-" * 90)
-                        mem_id = data['value']['id']
-                        ## Remove keys before using DeepDiff
-                        remove_keys = {'id', 'createdDateTime', 'version', 'lastModifiedDateTime'}
-                        for k in remove_keys:
-                            data['value'].pop(k, None)
-                        repo_data.pop('targetedMobileApps', None)
-
-                        diff = DeepDiff(data['value'], repo_data, ignore_order=True).get(
-                            'values_changed', {})
-
-                        ## If any changed values are found, push them to Intune
-                        if diff:
-                            print("Updating App configuration: " +
-                                  repo_data['displayName'] + ", values changed:")
-                            for key, value in diff.items():
-                                setting = re.search(
-                                    "\[(.*)\]", key).group(1).split("[")[-1]
-                                new_val = value['new_value']
-                                old_val = value['old_value']
-                                print(
-                                    f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                            request_data = json.dumps(repo_data)
-                            q_param = None
-                            makeapirequestPatch(endpoint + "/" + mem_id, token, q_param, request_data,status_code=204)
-                        else:
-                            print(
-                                'No difference found for App configuration: ' + repo_data['displayName'])
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_id, mem_assignments)
-                            update = update_assignment(assign_obj, mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['assignments'] = update
-                                post_assignment_update(request_data, mem_id,'deviceAppManagement/mobileAppConfigurations/',
-                                                        '/microsoft.graph.managedDeviceMobileAppConfiguration/assign',token)
-
-                    ## If App Configuration does not exist, create it and assign
+                repo_data = load_file(filename, f)
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                # If App Configuration exists, continue
+                data = {'value': ''}
+                if mem_data['value']:
+                    for val in mem_data['value']:
+                        if repo_data['@odata.type'] == val['@odata.type'] and \
+                                repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                if data['value']:
+                    print("-" * 90)
+                    mem_id = data['value']['id']
+                    # Remove keys before using DeepDiff
+                    data = remove_keys(data)
+                    repo_data.pop('targetedMobileApps', None)
+
+                    diff = DeepDiff(
+                        data['value'], repo_data, ignore_order=True).get(
+                        'values_changed', {})
+
+                    # If any changed values are found, push them to Intune
+                    if diff:
+                        diff_count += 1
+                        print("Updating App configuration: " +
+                              repo_data['displayName'] + ", values changed:")
+                        values = get_diff_output(diff)
+                        for value in values:
+                            print(value)
+                        request_data = json.dumps(repo_data)
+                        q_param = None
+                        makeapirequestPatch(
+                            ENDPOINT + "/" + mem_id,
+                            token,
+                            q_param,
+                            request_data,
+                            status_code=204)
                     else:
-                        print("-" * 90)
-                        print("App Configuration not found, creating: " +
-                              repo_data['displayName'])
-                        app_ids = {}
-                        ## If backup contains targeted apps, search for the app
-                        if repo_data['targetedMobileApps']:
-                            q_param = {"$filter": "(isof(" + "'"+str(repo_data['targetedMobileApps']['type']).replace('#', '') + "'" + '))',
-                                       "$search": repo_data['targetedMobileApps']['appName']}
-                            app_request = makeapirequest(
-                                app_endpoint, token, q_param)
-                            if app_request['value']:
-                                app_ids = app_request['value'][0]['id']
-                        ## If the app could be found and matches type and name in backup, continue to create
-                        if app_ids:
-                            repo_data.pop('targetedMobileApps')
-                            repo_data['targetedMobileApps'] = [app_ids]
-                            request_json = json.dumps(repo_data)
-                            post_request = makeapirequestPost(endpoint, token, q_param=None, jdata=request_json,status_code=201)
-                            mem_assign_obj = []
-                            assignment = update_assignment(assign_obj, mem_assign_obj,token)
-                            if assignment is not None:
-                                request_data = {}
-                                request_data['assignments'] = assignment
-                                post_assignment_update(request_data, post_request['id'],'deviceAppManagement/mobileAppConfigurations/',
-                                                        '/microsoft.graph.managedDeviceMobileAppConfiguration/assign',token)
-                            print("App Configuration created with id: " +
-                                  post_request['id'])
-                        else:
-                            print("App configured in App Configuration profile could not be found, skipping creation")
+                        print(
+                            'No difference found for App configuration: ' +
+                            repo_data['displayName'])
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_id, mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {}
+                            request_data['assignments'] = update
+                            post_assignment_update(
+                                request_data,
+                                mem_id,
+                                'deviceAppManagement/mobileAppConfigurations/',
+                                '/microsoft.graph.managedDeviceMobileAppConfiguration/assign',
+                                token)
+
+                # If App Configuration does not exist, create it and assign
+                else:
+                    print("-" * 90)
+                    print("App Configuration not found, creating: " +
+                          repo_data['displayName'])
+                    app_ids = {}
+                    # If backup contains targeted apps, search for the app
+                    if repo_data['targetedMobileApps']:
+                        q_param = {
+                            "$filter": "(isof(" + "'" + str(
+                                repo_data['targetedMobileApps']['type']).replace(
+                                '#',
+                                '') + "'" + '))',
+                            "$search": repo_data['targetedMobileApps']['appName']}
+                        app_request = makeapirequest(
+                            APP_ENDPOINT, token, q_param)
+                        if app_request['value']:
+                            app_ids = app_request['value'][0]['id']
+                    # If the app could be found and matches type and name in
+                    # backup, continue to create
+                    if app_ids:
+                        repo_data.pop('targetedMobileApps')
+                        repo_data['targetedMobileApps'] = [app_ids]
+                        request_json = json.dumps(repo_data)
+                        post_request = makeapirequestPost(
+                            ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                        mem_assign_obj = []
+                        assignment = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if assignment is not None:
+                            request_data = {}
+                            request_data['assignments'] = assignment
+                            post_assignment_update(
+                                request_data,
+                                post_request['id'],
+                                'deviceAppManagement/mobileAppConfigurations/',
+                                '/microsoft.graph.managedDeviceMobileAppConfiguration/assign',
+                                token)
+                        print("App Configuration created with id: " +
+                              post_request['id'])
+                    else:
+                        print(
+                            "App configured in App Configuration profile could not be found, skipping creation")
+
+    return diff_count
diff --git a/src/IntuneCD/update_appProtection.py b/src/IntuneCD/update_appProtection.py
index e91bad2e..a5b1c241 100644
--- a/src/IntuneCD/update_appProtection.py
+++ b/src/IntuneCD/update_appProtection.py
@@ -1,137 +1,163 @@
 #!/usr/bin/env python3
 
 """
-This module updates all App Protection Polices in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all App Protection Policies in Intune.
 """
 
 import json
 import os
-import yaml
-import re
 
-from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
-from .graph_batch import batch_assignment,get_object_assignment
-from .update_assignment import update_assignment,post_assignment_update
 from deepdiff import DeepDiff
+from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
+from .graph_batch import batch_assignment, get_object_assignment
+from .update_assignment import update_assignment, post_assignment_update
+from .remove_keys import remove_keys
+from .get_diff_output import get_diff_output
+from .check_file import check_file
+from .load_file import load_file
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/"
 
 
 def update(path, token, assignment=False):
-
-    ## Set App Protection path
-    configpath = path+"/"+"App Protection/"
-    ## If App Configuration path exists, continue
-    if os.path.exists(configpath) == True:
-
-        ## Get App Protections
-        mem_data = makeapirequest(f'{endpoint}managedAppPolicies', token)
-        ## Get current assignments
-        mem_assignments = batch_assignment(mem_data,'deviceAppManagement/','/assignments',token,app_protection=True)
+    """
+    This function updates all App Protection Polices 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
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set App Protection path
+    configpath = path + "/" + "App Protection/"
+    # If App Configuration path exists, continue
+    if os.path.exists(configpath):
+
+        # Get App Protections
+        mem_data = makeapirequest(f'{ENDPOINT}managedAppPolicies', token)
+        # Get current assignments
+        mem_assignments = batch_assignment(
+            mem_data,
+            'deviceAppManagement/',
+            '/assignments',
+            token,
+            app_protection=True)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
-                continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
+            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
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                        
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-
-                    if repo_data:
-                        if repo_data['@odata.type'] == "#microsoft.graph.mdmWindowsInformationProtectionPolicy":
-                            platform = "mdmWindowsInformationProtectionPolicies"
-                        elif repo_data['@odata.type'] == "#microsoft.graph.windowsInformationProtectionPolicy":
-                            platform = "windowsInformationProtectionPolicies"
-                        else:
-                            platform = f"{str(repo_data['@odata.type']).split('.')[2]}s"
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    ## If App Protection exists, continue
-                    data = {'value':''}
-                    if mem_data['value']:
-                        for val in mem_data['value']:
-                            if 'targetedAppManagementLevels' in val and 'targetedAppManagementLevels' in repo_data:
-                                if repo_data['targetedAppManagementLevels'] == val['targetedAppManagementLevels'] and \
+                repo_data = load_file(filename, f)
+
+                if repo_data:
+                    if repo_data['@odata.type'] == "#microsoft.graph.mdmWindowsInformationProtectionPolicy":
+                        platform = "mdmWindowsInformationProtectionPolicies"
+                    elif repo_data['@odata.type'] == "#microsoft.graph.windowsInformationProtectionPolicy":
+                        platform = "windowsInformationProtectionPolicies"
+                    else:
+                        platform = f"{str(repo_data['@odata.type']).split('.')[2]}s"
+
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                # If App Protection exists, continue
+                data = {'value': ''}
+                if mem_data['value']:
+                    for val in mem_data['value']:
+                        if 'targetedAppManagementLevels' in val and 'targetedAppManagementLevels' in repo_data:
+                            if repo_data['targetedAppManagementLevels'] == val['targetedAppManagementLevels'] and \
                                     repo_data['displayName'] == val['displayName']:
-                                    data['value'] = val
-                            elif repo_data['@odata.type'] == val['@odata.type'] and \
-                                repo_data['displayName'] == val['displayName']:
                                 data['value'] = val
-                                
-                    if data['value']:
-                        print("-" * 90)
-                        mem_id = data['value']['id']
-                        platform=""
-                        # Remove keys before using DeepDiff
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime'}
-                        for k in remove_keys:
-                            data['value'].pop(k, None)
-
-                        diff = DeepDiff(data['value'], repo_data, ignore_order=True).get('values_changed', {})
-
-                        ## If any changed values are found, push them to Intune
-                        if diff:
-                            print("Updating App protection: " + \
-                                  repo_data['displayName'] + ", values changed:")
-                            for key, value in diff.items():
-                                setting = re.search("\[(.*)\]", key).group(1)
-                                new_val = value['new_value']
-                                old_val = value['old_value']
-                                print(
-                                    f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                            request_data = json.dumps(repo_data)
-                            q_param = None
-                            makeapirequestPatch(f'{endpoint}{platform}/{mem_id}', token,q_param,request_data,status_code=204)
-                        else:
-                            print(
-                                'No difference found for App protection: ' + repo_data['displayName'])
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_id,mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['assignments'] = update
-                                post_assignment_update(request_data,mem_id,f'deviceAppManagement/{platform}','assign',token,status_code=204)
-
-                    ## If App Protection does not exist, create it and assign
+                        elif repo_data['@odata.type'] == val['@odata.type'] and \
+                                repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                if data['value']:
+                    print("-" * 90)
+                    mem_id = data['value']['id']
+                    # Remove keys before using DeepDiff
+                    data['value'] = remove_keys(data['value'])
+
+                    diff = DeepDiff(
+                        data['value'],
+                        repo_data,
+                        ignore_order=True).get(
+                        'values_changed',
+                        {})
+
+                    # If any changed values are found, push them to Intune
+                    if diff:
+                        diff_count += 1
+                        print(
+                            "Updating App protection: " +
+                            repo_data['displayName'] +
+                            ", values changed:")
+                        values = get_diff_output(diff)
+                        for value in values:
+                            print(value)
+                        request_data = json.dumps(repo_data)
+                        q_param = None
+                        makeapirequestPatch(
+                            f'{ENDPOINT}{platform}/{mem_id}',
+                            token,
+                            q_param,
+                            request_data,
+                            status_code=204)
                     else:
-                        print("-" * 90)
-                        print("App Protection not found, creating policy: " + \
-                              repo_data['displayName'])
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(f'{endpoint}managedAppPolicies', token,q_param=None,jdata=request_json,status_code=201)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
-                            request_data = {}
-                            request_data['assignments'] = assignment
-                            post_assignment_update(request_data,post_request['id'],f'deviceAppManagement/{platform}','assign',token,status_code=204)
-                        print("App Protection created with id: " + post_request['id'])
\ No newline at end of file
+                        print(
+                            'No difference found for App protection: ' +
+                            repo_data['displayName'])
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_id, mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {'assignments': update}
+                            post_assignment_update(
+                                request_data,
+                                mem_id,
+                                f'deviceAppManagement/{platform}',
+                                'assign',
+                                token,
+                                status_code=204)
+
+                # If App Protection does not exist, create it and assign
+                else:
+                    print("-" * 90)
+                    print("App Protection not found, creating policy: " +
+                          repo_data['displayName'])
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        f'{ENDPOINT}managedAppPolicies',
+                        token,
+                        q_param=None,
+                        jdata=request_json,
+                        status_code=201)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {'assignments': assignment}
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            f'deviceAppManagement/{platform}',
+                            'assign',
+                            token,
+                            status_code=204)
+                    print(
+                        "App Protection created with id: " +
+                        post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_appleEnrollmentProfile.py b/src/IntuneCD/update_appleEnrollmentProfile.py
index 0bdcb233..4bea31c8 100644
--- a/src/IntuneCD/update_appleEnrollmentProfile.py
+++ b/src/IntuneCD/update_appleEnrollmentProfile.py
@@ -1,92 +1,97 @@
 #!/usr/bin/env python3
 
 """
-This module updates all App Configuration polices in Intune if the configuration in Intune differs from the JSON/YAML file.
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Apple Enrollment profiles in Intune.
 """
 
 import json
 import os
-import yaml
-import re
 
-from .graph_request import makeapirequest, makeapirequestPatch
 from deepdiff import DeepDiff
+from .graph_request import makeapirequest, makeapirequestPatch
+from .remove_keys import remove_keys
+from .check_file import check_file
+from .load_file import load_file
+from .get_diff_output import get_diff_output
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings/"
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings/"
 
 def update(path, token):
+    """
+    This function updates all Apple Enrollment Profiles 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
+    """
 
-    ## Set Apple Enrollment Profile path
-    configpath = path+"/"+"Enrollment Profiles/Apple/"
-    ## If Apple Enrollment Profile path exists, continue
-    if os.path.exists(configpath) == True:
-        ## Get IDs of all Apple Enrollment Profiles and add them to a list
+    diff_count = 0
+    # Set Apple Enrollment Profile path
+    configpath = path + "/" + "Enrollment Profiles/Apple/"
+    # If Apple Enrollment Profile path exists, continue
+    if os.path.exists(configpath):
+        # Get IDs of all Apple Enrollment Profiles and add them to a list
         ids = []
-        mem_data_accounts = makeapirequest(endpoint, token)
+        mem_data_accounts = makeapirequest(ENDPOINT, token)
         for id in mem_data_accounts['value']:
             ids.append(id['id'])
 
         for profile in ids:
             for filename in os.listdir(configpath):
-                file = os.path.join(configpath, filename)
-                # If path is Directory, skip
-                if os.path.isdir(file):
+                file = check_file(configpath, filename)
+                if file is False:
                     continue
-                # If file is .DS_Store, skip
-                if filename == ".DS_Store":
-                    continue
-                # If file is .md, skip
-                if filename.endswith(".md"):
-                    continue
-
-                ## Check which format the file is saved as then open file, load data and set query parameter
+                # Check which format the file is saved as then open file, load
+                # data and set query parameter
                 with open(file) as f:
-                        if filename.endswith(".yaml"):
-                            data = json.dumps(yaml.safe_load(f))
-                            repo_data = json.loads(data)
-                            q_param = {"$filter": "displayName eq " +
-                                "'" + repo_data['displayName'] + "'"}
+                    repo_data = load_file(filename, f)
+                    q_param = {"$filter": "displayName eq " +
+                               "'" + repo_data['displayName'] + "'"}
 
-                        elif filename.endswith(".json"):
-                            f = open(file)
-                            repo_data = json.load(f)
-                            q_param = {"$filter": "displayName eq " + "'" + repo_data['displayName'] + "'"}
+                    # Get Apple Enrollment Profile with query parameter
+                    profile_data = makeapirequest(
+                        ENDPOINT + profile + '/enrollmentProfiles', token, q_param)
 
-                        ## Get Apple Enrollment Profile with query parameter
-                        profile_data = makeapirequest(
-                            endpoint + profile + '/enrollmentProfiles', token, q_param)
+                    # If Apple Enrollment Profile exists, continue
+                    if profile_data['value']:
+                        print("-" * 90)
+                        pid = profile_data['value'][0]['id']
+                        # Remove keys before using DeepDiff
+                        profile_data['value'][0] = remove_keys(profile_data['value'][0])
 
-                        ## If Apple Enrollment Profile exists, continue
-                        if profile_data['value']:
-                            print("-" * 90)
-                            pid = profile_data['value'][0]['id']
-                            ## Remove keys before using DeepDiff
-                            remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime'}
-                            for k in remove_keys:
-                                profile_data['value'][0].pop(k, None)
+                        diff = DeepDiff(
+                            profile_data['value'][0],
+                            repo_data,
+                            ignore_order=True).get(
+                            'values_changed',
+                            {})
 
-                            diff = DeepDiff(profile_data['value'][0], repo_data, ignore_order=True).get('values_changed', {})
+                        # If any changed values are found, push them to Intune
+                        if diff:
+                            diff_count += 1
+                            print(
+                                "Updating Apple Enrollment profile: " +
+                                repo_data['displayName'] +
+                                ", values changed:")
+                            values = get_diff_output(diff)
+                            for val in values:
+                                print(val)
+                            request_data = json.dumps(repo_data)
+                            q_param = None
+                            makeapirequestPatch(
+                                ENDPOINT +
+                                profile +
+                                "/enrollmentProfiles/" +
+                                pid,
+                                token,
+                                q_param,
+                                request_data,
+                                status_code=204)
+                        else:
+                            print(
+                                'No difference found for Apple Enrollment profile: ' +
+                                repo_data['displayName'])
 
-                            ## If any changed values are found, push them to Intune
-                            if diff:
-                                print("Updating Apple Enrollment profile: " + \
-                                      repo_data['displayName'] + ", values changed:")
-                                for key, value in diff.items():
-                                    setting = re.search(
-                                        "\[(.*)\]", key).group(1)
-                                    new_val = value['new_value']
-                                    old_val = value['old_value']
-                                    print(
-                                        f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                request_data = json.dumps(repo_data)
-                                q_param = None
-                                makeapirequestPatch(endpoint + profile + "/enrollmentProfiles/" + pid, token,q_param,request_data,status_code=204)
-                            else:
-                                print('No difference found for Apple Enrollment profile: ' + repo_data['displayName'])
\ No newline at end of file
+    return diff_count
diff --git a/src/IntuneCD/update_assignment.py b/src/IntuneCD/update_assignment.py
index 0540cb16..04955f10 100644
--- a/src/IntuneCD/update_assignment.py
+++ b/src/IntuneCD/update_assignment.py
@@ -1,41 +1,22 @@
+#!/usr/bin/env python3
+
 """
-This module is used to update assignments for configurations in Intune
-
-Parameters get_added_removed
-----------
-diff_object : list
-    Object to check for added assignments
-
-Parameters update_assignment
-----------
-mem : list
-    Configuration data from Intune
-repo : list
-    Configuration data from repo
-token : str
-    OAuth token used for authentication
-
-Parameters post_assignment_update
-----------
-object : list
-    Assignment object for configuration
-id : str
-    id of the configuration to update
-url : str
-    Graph endpoint to configuration
-extra_url : str
-    Used if any extra url is needed such as /assign
-token : str
-    OAuth token used for authentication
-status_code : int
-    Used if assignment response is not 200
+This module contains the functions used to update assignments in Intune.
 """
 
 import json
 from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPost
 
+
 def get_added_removed(diff_object) -> list:
+    """
+    This function is used to get added and removed assignments from the diff object.
+
+    :param diff_object: Object to check for added assignments
+    :return: string of added and removed assignments
+    """
+
     update = []
     for root in diff_object:
         intent = ''
@@ -61,45 +42,59 @@ def get_added_removed(diff_object) -> list:
             if diff_object[root]['target']['@odata.type'] == '#microsoft.graph.allLicensedUsersAssignmentTarget':
                 target = 'All Users'
 
-        update.append(f"intent: {intent}, Filter ID: {filterID}, Filter Type: {filterType}, target: {target}")
+        update.append(
+            f"intent: {intent}, Filter ID: {filterID}, Filter Type: {filterType}, target: {target}")
 
     return update
 
-def update_assignment(repo,mem,token) -> list:
 
-    diff = DeepDiff(mem,repo,ignore_order=True)
+def update_assignment(repo, mem, token) -> list:
+    """
+    This function is used to update assignments for configurations in Intune.
+
+    :param repo: Configuration data from repo
+    :param mem: Configuration data from Intune
+    :param token: OAuth token used for authentication
+    :return: If update is true, return repo data, else return None
+    """
+
+    diff = DeepDiff(mem, repo, ignore_order=True)
     added = diff.get('iterable_item_added', {})
     update = False
-
     if diff:
         for val in repo:
-            ## Request group id based on group name
+            # Request group id based on group name
             if 'groupName' in val['target']:
-                    request = makeapirequest("https://graph.microsoft.com/beta/groups", token, 
-                                            {"$filter": "displayName eq " + "'" + val['target']['groupName'] + "'"})
-                    if request['value']:
-                        val['target'].pop('groupName')
-                        val['target']['groupId'] = request['value'][0]['id']
-
-            ## Request filter id based on filter name
+                request = makeapirequest(
+                    "https://graph.microsoft.com/beta/groups", token, {
+                        "$filter": "displayName eq " + "'" + val['target']['groupName'] + "'"})
+                if request['value']:
+                    val['target'].pop('groupName')
+                    val['target']['groupId'] = request['value'][0]['id']
+
+            # Request filter id based on filter name
             if val['target']['deviceAndAppManagementAssignmentFilterId']:
-                filters = makeapirequest("https://graph.microsoft.com/beta/deviceManagement/assignmentFilters", token)
+                filters = makeapirequest(
+                    "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters", token)
                 for filter in filters['value']:
                     if val['target']['deviceAndAppManagementAssignmentFilterId'] == filter['displayName']:
                         val['target']['deviceAndAppManagementAssignmentFilterId'] = filter['id']
 
-                ## If filter is None, remove keys
+                # If filter is None, remove keys
                 if val['target']['deviceAndAppManagementAssignmentFilterId'] is None:
-                    val['target'].pop('deviceAndAppManagementAssignmentFilterId')
-                    val['target'].pop('deviceAndAppManagementAssignmentFilterType')
+                    val['target'].pop(
+                        'deviceAndAppManagementAssignmentFilterId')
+                    val['target'].pop(
+                        'deviceAndAppManagementAssignmentFilterType')
 
         for val in repo:
-            if 'groupId' in val['target'] or '#microsoft.graph.allDevicesAssignmentTarget' in val['target']['@odata.type'] \
-            or '#microsoft.graph.allLicensedUsersAssignmentTarget' in val['target']['@odata.type']:
+            if 'groupId' in val['target'] or '#microsoft.graph.allDevicesAssignmentTarget' in val['target'][
+                '@odata.type'] \
+                    or '#microsoft.graph.allLicensedUsersAssignmentTarget' in val['target']['@odata.type']:
                 update = True
 
         if update is True:
-            ## Print added assignments
+            # Print added assignments
             if added:
                 print('Updating assignments, added assignments:')
                 updates = get_added_removed(added)
@@ -109,7 +104,25 @@ def update_assignment(repo,mem,token) -> list:
             else:
                 return None
 
-def post_assignment_update(object,id,url,extra_url,token,status_code=200):
+
+def post_assignment_update(object, id, url, extra_url, token, status_code=200):
+    """
+    This function is used to post assignment update to Intune.
+
+    :param object: Assignment object for configuration
+    :param id: id of the configuration to update
+    :param url: Graph endpoint to configuration
+    :param extra_url: Used if any extra url is needed such as /assign
+    :param token: OAuth token used for authentication
+    :param status_code: Used if assignment response is not 200
+    :return:
+    """
+
     request_json = json.dumps(object)
     url = f'https://graph.microsoft.com/beta/{url}/{id}/{extra_url}'
-    makeapirequestPost(url, token, q_param=None, jdata=request_json, status_code=status_code)
\ No newline at end of file
+    makeapirequestPost(
+        url,
+        token,
+        q_param=None,
+        jdata=request_json,
+        status_code=status_code)
diff --git a/src/IntuneCD/update_assignmentFilter.py b/src/IntuneCD/update_assignmentFilter.py
index 77950d61..af02b0f5 100644
--- a/src/IntuneCD/update_assignmentFilter.py
+++ b/src/IntuneCD/update_assignmentFilter.py
@@ -1,98 +1,96 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Filters in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Filters in Intune.
 """
 
 import json
 import os
-import yaml
-import re
 
-from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from deepdiff import DeepDiff
+from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
+from .remove_keys import remove_keys
+from .load_file import load_file
+from .check_file import check_file
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters"
+
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters"
 
 
 def update(path, token):
+    """
+    This function updates all Filters 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
+    """
 
-    ## Set Filters path
-    configpath = path+"/"+"Filters"
-    ## If App Configuration path exists, continue
-    if os.path.exists(configpath) == True:
-        ## get all filters
-        mem_data = makeapirequest(endpoint, token)
+    diff_count = 0
+    # Set Filters path
+    configpath = path + "/" + "Filters"
+    # If App Configuration path exists, continue
+    if os.path.exists(configpath):
+        # get all filters
+        mem_data = makeapirequest(ENDPOINT, token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
+            file = check_file(configpath, filename)
+            if file is False:
                 continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
-                continue
-
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-                  
-                    filter_value = {}
-
-                    ## If Filter exists, continue
-                    if mem_data['value']:
-                        for val in mem_data['value']:
-                            if repo_data['displayName'] == val['displayName']:
-                                filter_value = val
-
-                    if filter_value:
-                        print("-" * 90)
-                        filter_id = filter_value['id']
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime'}
-                        for k in remove_keys:
-                            filter_value.pop(k, None)
-
-                        diff = DeepDiff(filter_value, repo_data, ignore_order=True).get('values_changed', {})
-
-                        ## If any changed values are found, push them to Intune
-                        if diff:
-                            print("Updating Filter: " + \
-                                  repo_data['displayName'] + ", values changed:")
-                            for key, value in diff.items():
-                                setting = re.search("\[(.*)\]", key).group(1)
-                                new_val = value['new_value']
-                                old_val = value['old_value']
-                                print(
-                                    f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                            repo_data.pop("platform", None)
-                            request_data = json.dumps(repo_data)
-                            makeapirequestPatch(endpoint + "/" + filter_id, token,q_param=None,jdata=request_data)
-                        else:
-                            print('No difference found for Filter: ' + \
-                                  repo_data['displayName'])
-
-                    ## If Filter does not exist, create it
+                repo_data = load_file(filename, f)
+
+                filter_value = {}
+
+                # If Filter exists, continue
+                if mem_data['value']:
+                    for val in mem_data['value']:
+                        if repo_data['displayName'] == val['displayName']:
+                            filter_value = val
+
+                if filter_value:
+                    print("-" * 90)
+                    filter_id = filter_value['id']
+                    filter_value = remove_keys(filter_value)
+
+                    diff = DeepDiff(
+                        filter_value, repo_data, ignore_order=True).get(
+                        'values_changed', {})
+
+                    # If any changed values are found, push them to Intune
+                    if diff:
+                        diff_count += 1
+                        print("Updating Filter: " +
+                              repo_data['displayName'] + ", values changed:")
+                        values = get_diff_output(diff)
+                        for value in values:
+                            print(value)
+                        repo_data.pop("platform", None)
+                        request_data = json.dumps(repo_data)
+                        makeapirequestPatch(
+                            ENDPOINT + "/" + filter_id,
+                            token,
+                            q_param=None,
+                            jdata=request_data)
                     else:
-                        print("-" * 90)
-                        print(
-                            "Assignment filter not found, creating filter: " + repo_data['displayName'])
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=request_json,status_code=201)
-                        print("Assignemnt filter created with id: " + post_request['id'])
\ No newline at end of file
+                        print('No difference found for Filter: ' +
+                              repo_data['displayName'])
+
+                # If Filter does not exist, create it
+                else:
+                    print("-" * 90)
+                    print(
+                        "Assignment filter not found, creating filter: " +
+                        repo_data['displayName'])
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                    print(
+                        "Assignment filter created with id: " +
+                        post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_compliance.py b/src/IntuneCD/update_compliance.py
index 900fe687..5e23bf98 100644
--- a/src/IntuneCD/update_compliance.py
+++ b/src/IntuneCD/update_compliance.py
@@ -1,164 +1,198 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Compliance Polices in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Compliance Policies in Intune.
 """
 
 import json
 import os
-import yaml
-import re
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from .graph_batch import batch_assignment, get_object_assignment
-from .update_assignment import update_assignment,post_assignment_update
-from deepdiff import DeepDiff
+from .update_assignment import update_assignment, post_assignment_update
+from .remove_keys import remove_keys
+from .load_file import load_file
+from .check_file import check_file
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies"
 
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies"
 
-def update(path, token, assignment=False):
 
-    ## Set Compliance Policy path
-    configpath = path+"/"+"Compliance Policies/Policies/"
-    ## If App Configuration path exists, continue
-    if os.path.exists(configpath) == True:
-        ## Get compliance policies
-        q_param = {"expand": "scheduledActionsForRule($expand=scheduledActionConfigurations)"}
-        mem_data = makeapirequest(endpoint, token, q_param)
-        ## Get current assignments
-        mem_assignments = batch_assignment(mem_data,'deviceManagement/deviceCompliancePolicies/','/assignments',token)
+def update(path, token, assignment=False):
+    """
+    This function updates all Compliance Polices 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
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set Compliance Policy path
+    configpath = path + "/" + "Compliance Policies/Policies/"
+    # If App Configuration path exists, continue
+    if os.path.exists(configpath):
+        # Get compliance policies
+        q_param = {
+            "expand": "scheduledActionsForRule($expand=scheduledActionConfigurations)"}
+        mem_data = makeapirequest(ENDPOINT, token, q_param)
+        # Get current assignments
+        mem_assignments = batch_assignment(
+            mem_data,
+            'deviceManagement/deviceCompliancePolicies/',
+            '/assignments',
+            token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
-                continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
+            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
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    ## If Compliance Policy exists, continue
-                    data = {'value':''}
-                    if mem_data['value']:
-                        for val in mem_data['value']:
-                            if repo_data['@odata.type'] == val['@odata.type'] and \
-                               repo_data['displayName'] == val['displayName']:
-                                data['value'] = val
-                                
-                    if data['value']:
-                        print("-" * 90)
-                        mem_id = data['value']['id']
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime'}
-                        for k in remove_keys:
-                            data['value'].pop(k, None)
-
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime','@odata.context',
-                                        'scheduledActionConfigurations@odata.context','scheduledActionsForRule@odata.context'}
-                        for k in remove_keys:
-                            data['value'].pop(k, None)
-                            if data['value']['scheduledActionsForRule']:
-                                for rule in data['value']['scheduledActionsForRule']:
-                                    rule.pop(k, None)
-                                for scheduled_config in data['value']['scheduledActionsForRule'][0]['scheduledActionConfigurations']:
-                                    scheduled_config.pop(k, None)
-
-                        diff = DeepDiff(data['value'], repo_data, ignore_order=True,
-                        exclude_paths="root['scheduledActionsForRule'][0]['scheduledActionConfigurations']").get('values_changed', {})
-
-                        ## If any changed values are found, push them to Intune
-                        if diff:
-                            print("Updating Compliance policy: " + \
-                                  repo_data['displayName'] + ", values changed:")
-                            for key, value in diff.items():
-                                setting = re.search("\[(.*)\]", key).group(1)
-                                new_val = value['new_value']
-                                old_val = value['old_value']
-                                print(f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                
-                            scheduled_actions = repo_data['scheduledActionsForRule']
-                            repo_data.pop('scheduledActionsForRule', None)
-                            request_data = json.dumps(repo_data)
-                            q_param = None
-                            makeapirequestPatch(endpoint + "/" + mem_id, token,q_param,request_data,status_code=204)
-                            repo_data['scheduledActionsForRule'] = scheduled_actions
-
-                        if repo_data['scheduledActionsForRule']:
-                            for mem_rule, repo_rule in zip(data['value']['scheduledActionsForRule'],repo_data['scheduledActionsForRule']):
-                                rdiff = DeepDiff(mem_rule, repo_rule, ignore_order=True).get('values_changed', {})
-                            if rdiff:
-                                print("Updating rules for Compliance Policy: " + \
-                                      repo_data['displayName'] + ", values changed:")
-                                for key, value in rdiff.items():
-                                    setting = re.search(
-                                        "\[(.*)\]", key).group(1).split("[")[-1]
-                                    new_val = value['new_value']
-                                    old_val = value['old_value']
-                                    print(
-                                        f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                request_data = {
-                                    "deviceComplianceScheduledActionForRules": [
-                                        {
-                                            "ruleName": "PasswordRequired",
-                                            "scheduledActionConfigurations":
-                                                repo_data['scheduledActionsForRule'][0]['scheduledActionConfigurations']
-                                        }
-                                    ]
-                                }
-                                request_json = json.dumps(request_data)
-                                q_param = None
-                                makeapirequestPost(endpoint + "/" + mem_id + "/scheduleActionsForRules", token,q_param,request_json)
-                            else:
-                                print('No difference in rules found for Compliance policy: ' + repo_data['displayName'])
-                        else:
-                            print('No difference found for Compliance policy: ' + repo_data['displayName'])
+                repo_data = load_file(filename, f)
+
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                # If Compliance Policy exists, continue
+                data = {'value': ''}
+                if mem_data['value']:
+                    for val in mem_data['value']:
+                        if repo_data['@odata.type'] == val['@odata.type'] and \
+                                repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                if data['value']:
+                    print("-" * 90)
+                    mem_id = data['value']['id']
+                    data['value'] = remove_keys(data['value'])
+
+                    if data['value']['scheduledActionsForRule']:
+                        for rule in data['value']['scheduledActionsForRule']:
+                            remove_keys(rule)
+                        for scheduled_config in data['value']['scheduledActionsForRule'][0][
+                                'scheduledActionConfigurations']:
+                            remove_keys(scheduled_config)
+
+                    diff = DeepDiff(
+                        data['value'],
+                        repo_data,
+                        ignore_order=True,
+                        exclude_paths="root['scheduledActionsForRule'][0]['scheduledActionConfigurations']").get(
+                        'values_changed',
+                        {})
+
+                    # If any changed values are found, push them to Intune
+                    if diff:
+                        diff_count += 1
+                        print("Updating Compliance policy: " +
+                              repo_data['displayName'] + ", values changed:")
+                        values = get_diff_output(diff)
+                        for value in values:
+                            print(value)
+
+                        scheduled_actions = repo_data['scheduledActionsForRule']
+                        repo_data.pop('scheduledActionsForRule', None)
+                        request_data = json.dumps(repo_data)
+                        q_param = None
+                        makeapirequestPatch(
+                            ENDPOINT + "/" + mem_id,
+                            token,
+                            q_param,
+                            request_data,
+                            status_code=204)
+                        repo_data['scheduledActionsForRule'] = scheduled_actions
 
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_id,mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['assignments'] = update
-                                post_assignment_update(request_data,mem_id,'deviceManagement/deviceCompliancePolicies','assign',token)
-
-                    ## If Compliance Policy does not exist, create it and assign
                     else:
-                        print("-" * 90)
                         print(
-                            "Compliance Policy not found, creating Policy: " + repo_data['displayName'])
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=request_json,status_code=201)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
+                            'No difference found for Compliance policy: ' +
+                            repo_data['displayName'])
+
+                    if repo_data['scheduledActionsForRule']:
+                        for mem_rule, repo_rule in zip(
+                                data['value']['scheduledActionsForRule'], repo_data['scheduledActionsForRule']):
+                            rdiff = DeepDiff(
+                                mem_rule, repo_rule, ignore_order=True).get(
+                                'values_changed', {})
+                        if rdiff:
+                            diff_count += 1
+                            print(
+                                "Updating rules for Compliance Policy: " +
+                                repo_data['displayName'] +
+                                ", values changed:")
+                            values = get_diff_output(rdiff)
+                            for value in values:
+                                print(value)
+                            request_data = {
+                                "deviceComplianceScheduledActionForRules": [
+                                    {
+                                        "ruleName": "PasswordRequired",
+                                        "scheduledActionConfigurations":
+                                            repo_data['scheduledActionsForRule'][0]['scheduledActionConfigurations']
+                                    }
+                                ]
+                            }
+                            request_json = json.dumps(request_data)
+                            q_param = None
+                            makeapirequestPost(
+                                ENDPOINT +
+                                "/" +
+                                mem_id +
+                                "/scheduleActionsForRules",
+                                token,
+                                q_param,
+                                request_json)
+                        else:
+                            print(
+                                'No difference in rules found for Compliance policy: ' +
+                                repo_data['displayName'])
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_id, mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
                             request_data = {}
-                            request_data['assignments'] = assignment
-                            post_assignment_update(request_data,post_request['id'],'deviceManagement/deviceCompliancePolicies','assign',token)
-                        print("Compliance Policy created with id: " + post_request['id'])
\ No newline at end of file
+                            request_data['assignments'] = update
+                            post_assignment_update(
+                                request_data,
+                                mem_id,
+                                'deviceManagement/deviceCompliancePolicies',
+                                'assign',
+                                token)
+
+                # If Compliance Policy does not exist, create it and assign
+                else:
+                    print("-" * 90)
+                    print(
+                        "Compliance Policy not found, creating Policy: " +
+                        repo_data['displayName'])
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {}
+                        request_data['assignments'] = assignment
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            'deviceManagement/deviceCompliancePolicies',
+                            'assign',
+                            token)
+                    print(
+                        "Compliance Policy created with id: " +
+                        post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_configurationPolicies.py b/src/IntuneCD/update_configurationPolicies.py
index 28a35abe..2edcd9e3 100644
--- a/src/IntuneCD/update_configurationPolicies.py
+++ b/src/IntuneCD/update_configurationPolicies.py
@@ -1,123 +1,148 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Settings Catalog configurations in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Settings Catalog assignments in Intune,
 """
 
 import json
 import os
-import yaml
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPut, makeapirequestPost
 from .graph_batch import batch_assignment, get_object_assignment
 from .update_assignment import update_assignment, post_assignment_update
-from deepdiff import DeepDiff
+from .check_file import check_file
+from .load_file import load_file
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies"
 
 
 def update(path, token, assignment=False):
-
-    ## Set Settings Catalog path
-    configpath = path+"/"+"Settings Catalog/"
-
-    if os.path.exists(configpath) == True:
-        ## Get configurations policies
-        mem_data = makeapirequest(endpoint, token)
-        ## Get current assignments
-        mem_assignments = batch_assignment(mem_data,'deviceManagement/configurationPolicies/','/assignments',token)
+    """
+    This function updates all Settings Catalog configurations 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
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set Settings Catalog path
+    configpath = path + "/" + "Settings Catalog/"
+
+    if os.path.exists(configpath):
+        # Get configurations policies
+        mem_data = makeapirequest(ENDPOINT, token)
+        # Get current assignments
+        mem_assignments = batch_assignment(
+            mem_data,
+            'deviceManagement/configurationPolicies/',
+            '/assignments',
+            token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
+            file = check_file(configpath, filename)
+            if file is False:
                 continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
-                continue
-
             (name, ext) = os.path.splitext(filename)
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    data = {'value':''}
-                    if mem_data['value']:
-                        for val in mem_data['value']:
-                            if repo_data['name'] == val['name']:
-                                data['value'] = val
-
-                    ## If Filter exists, continue
-                    if data['value']:
-                        print("-" * 90)
-                        ## Get Filter data from Intune
-                        mem_policy_data = makeapirequest(endpoint + "/" + data['value']['id'], token)
-                        ## Get Filter settings from Intune
-                        mem_policy_settings = makeapirequest(endpoint + "/" + data['value']['id'] + "/settings", token)
-                        ## Add settings to the data dictionary
-                        mem_policy_data['settings'] = mem_policy_settings['value']
-
-                        diff = DeepDiff(mem_policy_data, repo_data, ignore_order=True).get('values_changed', {})
-
-                        ## If any changed values are found, push them to Intune
-                        if diff:
-                            print("Updating Settings Catalog policy: " + \
-                                  name + ", values changed:")
-                            for key, value in diff.items():
-                                new_val = value['new_value']
-                                old_val = value['old_value']
-                                print(
-                                    f"New Value: {new_val}, Old Value: {old_val}")
-                            request_data = json.dumps(repo_data)
-                            q_param = None
-                            makeapirequestPut(endpoint + "/" + data['value']['id'], token,q_param,request_data,status_code=204)
-                        else:
-                            print(
-                                'No difference found for Settings Catalog policy: ' + name)
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(data['value']['id'],mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['assignments'] = update
-                                post_assignment_update(request_data,data['value']['id'],'deviceManagement/configurationPolicies','assign',token)
-
-                    ## If Configuration Policy does not exist, create it and assign
+                repo_data = load_file(filename, f)
+
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                data = {'value': ''}
+                if mem_data['value']:
+                    for val in mem_data['value']:
+                        if repo_data['name'] == val['name']:
+                            data['value'] = val
+
+                # If Filter exists, continue
+                if data['value']:
+                    print("-" * 90)
+                    # Get Filter data from Intune
+                    mem_policy_data = makeapirequest(
+                        ENDPOINT + "/" + data['value']['id'], token)
+                    # Get Filter settings from Intune
+                    mem_policy_settings = makeapirequest(
+                        ENDPOINT + "/" + data['value']['id'] + "/settings", token)
+                    # Add settings to the data dictionary
+                    mem_policy_data['settings'] = mem_policy_settings['value']
+
+                    diff = DeepDiff(
+                        mem_policy_data,
+                        repo_data,
+                        ignore_order=True).get(
+                        'values_changed',
+                        {})
+
+                    # If any changed values are found, push them to Intune
+                    if diff:
+                        diff_count += 1
+                        print("Updating Settings Catalog policy: " +
+                              name + ", values changed:")
+                        values = get_diff_output(diff)
+                        for value in values:
+                            print(value)
+                        request_data = json.dumps(repo_data)
+                        q_param = None
+                        makeapirequestPut(
+                            ENDPOINT +
+                            "/" +
+                            data['value']['id'],
+                            token,
+                            q_param,
+                            request_data,
+                            status_code=204)
                     else:
-                        print("-" * 90)
                         print(
-                            "Configuration Policy not found, creating Policy: " + repo_data['name'])
-                        repo_data.pop('settingCount', None)
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=request_json,status_code=201)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
-                            request_data = {}
-                            request_data['assignments'] = assignment
-                            post_assignment_update(request_data,post_request['id'],'deviceManagement/configurationPolicies','assign',token)
-                        print("Configuration Policy created with id: " + post_request['id'])
\ No newline at end of file
+                            'No difference found for Settings Catalog policy: ' + name)
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            data['value']['id'], mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {'assignments': update}
+                            post_assignment_update(
+                                request_data,
+                                data['value']['id'],
+                                'deviceManagement/configurationPolicies',
+                                'assign',
+                                token)
+
+                # If Configuration Policy does not exist, create it and assign
+                else:
+                    print("-" * 90)
+                    print(
+                        "Configuration Policy not found, creating Policy: " +
+                        repo_data['name'])
+                    repo_data.pop('settingCount', None)
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {'assignments': assignment}
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            'deviceManagement/configurationPolicies',
+                            'assign',
+                            token)
+                    print(
+                        "Configuration Policy created with id: " +
+                        post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_frontend.py b/src/IntuneCD/update_frontend.py
new file mode 100644
index 00000000..22743b25
--- /dev/null
+++ b/src/IntuneCD/update_frontend.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+
+"""
+This module is used to update the IntuneCD frontend.
+"""
+
+import os
+import requests
+
+
+def update_frontend(frontend, data):
+    """
+    This function updates the frontend with the number of configurations, diff count backup stream and update stream.
+
+    :param frontend: The frontend to update
+    :param data: The data to update the frontend with
+    """
+    API_KEY = os.environ.get("API_KEY")
+
+    if not API_KEY:
+        raise Exception("API_KEY environment variable is not set")
+
+    else:
+        headers = {'X-API-Key': API_KEY}
+        response = requests.post(frontend, json=data, headers=headers)
+
+        if response.status_code != 200:
+            raise Exception(f"Error updating frontend, {response.text}")
diff --git a/src/IntuneCD/update_managementIntents.py b/src/IntuneCD/update_managementIntents.py
index 51f67333..cb79ebcb 100644
--- a/src/IntuneCD/update_managementIntents.py
+++ b/src/IntuneCD/update_managementIntents.py
@@ -1,43 +1,47 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Endpoint Security configurations (intents) in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Endpoint Security configurations (intents) in Intune,
 """
 
 import json
 import os
-import yaml
 import glob
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPost
 from .graph_batch import batch_intents, batch_assignment, get_object_assignment
-from .update_assignment import update_assignment,post_assignment_update
-from deepdiff import DeepDiff
+from .update_assignment import update_assignment, post_assignment_update
+from .load_file import load_file
+from .get_diff_output import get_diff_output
 
-## Set MS Graph base endpoint
-baseEndpoint = "https://graph.microsoft.com/beta/deviceManagement"
+# Set MS Graph base endpoint
+BASE_ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement"
 
 
 def update(path, token, assignment=False):
-
-    ## Set Intent path
-    configpath = path+"/"+"Management Intents/"
-    ## If Intents path exists, continue
-    if os.path.exists(configpath) == True:
-        ## Get intents
-        intents = makeapirequest(baseEndpoint + "/intents", token)
-        intent_responses = batch_intents(intents,token)
-        ## Get current assignment
-        mem_assignments = batch_assignment(intents,'deviceManagement/intents/','/assignments',token)
-
-        ## Set glob pattern
+    """
+    This function updates all Endpoint Security configurations (intents) 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
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set Intent path
+    configpath = path + "/" + "Management Intents/"
+    # If Intents path exists, continue
+    if os.path.exists(configpath):
+        # Get intents
+        intents = makeapirequest(BASE_ENDPOINT + "/intents", token)
+        intent_responses = batch_intents(intents, token)
+        # Get current assignment
+        mem_assignments = batch_assignment(
+            intents, 'deviceManagement/intents/', '/assignments', token)
+
+        # Set glob pattern
         pattern = configpath + "*/*"
         for filename in glob.glob(pattern, recursive=True):
 
@@ -48,93 +52,119 @@ def update(path, token, assignment=False):
             if filename.endswith(".md"):
                 continue
 
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(filename) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-
-                    elif filename.endswith(".json"):
-                        f = open(filename)
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    mem_data = {}
-                    for intent in intent_responses['value']:
-                        if repo_data['displayName'] == intent['displayName'] and \
-                        repo_data['templateId'] == intent['templateId']:
-                            mem_data = intent
-
-                    ## If Intent exists, continue
-                    if mem_data:
-                        print("-" * 90)
-                        print("Checking if Intent: " + \
-                              repo_data['displayName'] + " has any upates")
-
-                        ## Compare category settings from Intune with JSON/YAML
-                        for mem_setting, repo_setting in zip(mem_data['settingsDelta'], repo_data['settingsDelta']):
-
-                            mem_setting_id = mem_setting['id']
-                            mem_setting.pop('id', None)
-
-                            diff = DeepDiff(mem_setting, repo_setting, ignore_order=True).get('values_changed', {})
-
-                            ## If any changed values are found, push them to Intune
-                            if diff:
-                                print("Updating Intent settings: " + \
-                                      repo_setting['definitionId'].split("_")[1] + ", values changed:")
-                                for key, value in diff.items():
-                                    new_val = value['new_value']
-                                    old_val = value['old_value']
-                                    print(
-                                        f"New Value: {new_val}, Old Value: {old_val}")
-                                ## Create dict that we will use as the request json
-                                if "value" not in repo_setting:
-                                    type = "valueJson"
-                                    value = repo_setting['valueJson']
-                                else:
-                                    type = "value"
-                                    value = repo_setting['value']
-                                settings = {
-                                    "settings": [
-                                        {
-                                            "id": mem_setting_id,
-                                            "definitionId": repo_setting['definitionId'],
-                                            "@odata.type": repo_setting['@odata.type'],
-                                            type: value
-                                        }
-                                    ]
-                                }
-                                request_data = json.dumps(settings)
-                                q_param = None
-                                makeapirequestPost(baseEndpoint + "/intents/" + mem_data['id'] + "/updateSettings", token,q_param,request_data,status_code=204)
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_data['id'],mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['assignments'] = update
-                                post_assignment_update(request_data,mem_data['id'],'deviceManagement/intents','assign',token,status_code=204)
-
-                    ## If Intent does not exist, create it and assign
-                    else:
-                        print("-" * 90)
-                        print("Intent not found, creating Intent: " + \
-                              repo_data['displayName'])
-                        template_id = repo_data['templateId']
-                        repo_data.pop('templateId')
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(baseEndpoint + "/templates/" + template_id + "/createInstance", token,q_param=None,jdata=request_json)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
-                            request_data = {}
-                            request_data['assignments'] = assignment
-                            post_assignment_update(request_data,post_request['id'],'deviceManagement/intents','assign',token,status_code=204)
-                        print("Intent created with id: " + post_request['id'])
\ No newline at end of file
+                repo_data = load_file(filename, f)
+
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                mem_data = {}
+                for intent in intent_responses['value']:
+                    if repo_data['displayName'] == intent['displayName'] and \
+                            repo_data['templateId'] == intent['templateId']:
+                        mem_data = intent
+
+                # If Intent exists, continue
+                if mem_data:
+                    print("-" * 90)
+                    print("Checking if Intent: " +
+                          repo_data['displayName'] + " has any updates")
+
+                    # Compare category settings from Intune with JSON/YAML
+                    for mem_setting, repo_setting in zip(
+                            mem_data['settingsDelta'], repo_data['settingsDelta']):
+
+                        mem_setting_id = mem_setting['id']
+                        mem_setting.pop('id', None)
+
+                        diff = DeepDiff(
+                            mem_setting, repo_setting, ignore_order=True).get(
+                            'values_changed', {})
+
+                        # If any changed values are found, push them to Intune
+                        if diff:
+                            diff_count += 1
+                            print(
+                                "Updating Intent settings: " +
+                                repo_setting['definitionId'].split("_")[1] +
+                                ", values changed:")
+                            values = get_diff_output(diff)
+                            for value in values:
+                                print(value)
+                            # Create dict that we will use as the request json
+                            if "value" not in repo_setting:
+                                type = "valueJson"
+                                value = repo_setting['valueJson']
+                            else:
+                                type = "value"
+                                value = repo_setting['value']
+                            settings = {
+                                "settings": [
+                                    {
+                                        "id": mem_setting_id,
+                                        "definitionId": repo_setting['definitionId'],
+                                        "@odata.type": repo_setting['@odata.type'],
+                                        type: value}]}
+                            request_data = json.dumps(settings)
+                            q_param = None
+                            makeapirequestPost(
+                                BASE_ENDPOINT +
+                                "/intents/" +
+                                mem_data['id'] +
+                                "/updateSettings",
+                                token,
+                                q_param,
+                                request_data,
+                                status_code=204)
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_data['id'], mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {'assignments': update}
+                            post_assignment_update(
+                                request_data,
+                                mem_data['id'],
+                                'deviceManagement/intents',
+                                'assign',
+                                token,
+                                status_code=204)
+
+                # If Intent does not exist, create it and assign
+                else:
+                    print("-" * 90)
+                    print("Intent not found, creating Intent: " +
+                          repo_data['displayName'])
+                    template_id = repo_data['templateId']
+                    repo_data.pop('templateId')
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        BASE_ENDPOINT +
+                        "/templates/" +
+                        template_id +
+                        "/createInstance",
+                        token,
+                        q_param=None,
+                        jdata=request_json)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {'assignments': assignment}
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            'deviceManagement/intents',
+                            'assign',
+                            token,
+                            status_code=204)
+                    print("Intent created with id: " + post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_notificationTemplate.py b/src/IntuneCD/update_notificationTemplate.py
index a50f703e..23bb242d 100644
--- a/src/IntuneCD/update_notificationTemplate.py
+++ b/src/IntuneCD/update_notificationTemplate.py
@@ -1,136 +1,172 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Notification Templates in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Notification Templates in Intune.
 """
 
 import json
 import os
-import yaml
-import re
 
-from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from deepdiff import DeepDiff
+from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
+from .check_file import check_file
+from .load_file import load_file
+from .remove_keys import remove_keys
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/notificationMessageTemplates"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/notificationMessageTemplates"
 
 
 def update(path, token):
+    """
+    This function updates all Notification Templates 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
+    """
 
-    ## Set Notification Template path
-    configpath = path+"/"+"Compliance Policies/Message Templates/"
-    ## If Notification Template path exists, continue
-    if os.path.exists(configpath) == True:
+    diff_count = 0
+    # Set Notification Template path
+    configpath = path + "/" + "Compliance Policies/Message Templates/"
+    # If Notification Template path exists, continue
+    if os.path.exists(configpath):
 
-        ## Get notification templates
-        mem_data = makeapirequest(endpoint, token)
+        # Get notification templates
+        mem_data = makeapirequest(ENDPOINT, token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
-                continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
+            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
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-                        q_param = {"$filter": "displayName eq " + "'" + repo_data['displayName'] + "'"}
-
-                    data = {'value':''}
-                    if mem_data['value']:
-                        for val in mem_data['value']:
-                            if repo_data['displayName'] == val['displayName']:
-                                data['value'] = val
-
-                    ## If Notification Template exists, continue
-                    if data['value']:
-                        print("-" * 90)
-                        ## Get Notification Template data from Intune
-                        q_param = "?$expand=localizedNotificationMessages"
-                        mem_template_data = makeapirequest(endpoint + "/" + data['value']['id'],token,q_param)
-                        ## Create dict to compare Intune data with JSON/YAML data
-                        repo_template_data = {
-                            "displayName": repo_data['displayName'],
-                            "brandingOptions": repo_data['brandingOptions'],
-                            "roleScopeTagIds": repo_data['roleScopeTagIds']
-                        }
-
-                        diff = DeepDiff(mem_template_data, repo_template_data, ignore_order=True).get('values_changed', {})
-
-                        ## If any changed values are found, push them to Intune
+                repo_data = load_file(filename, f)
+
+                data = {'value': ''}
+                if mem_data['value']:
+                    for val in mem_data['value']:
+                        if repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                # If Notification Template exists, continue
+                if data['value']:
+                    print("-" * 90)
+                    # Get Notification Template data from Intune
+                    q_param = "?$expand=localizedNotificationMessages"
+                    mem_template_data = makeapirequest(
+                        ENDPOINT + "/" + data['value']['id'], token, q_param)
+                    # Create dict to compare Intune data with JSON/YAML data
+                    repo_template_data = {
+                        "displayName": repo_data['displayName'],
+                        "brandingOptions": repo_data['brandingOptions'],
+                        "roleScopeTagIds": repo_data['roleScopeTagIds']
+                    }
+
+                    diff = DeepDiff(
+                        mem_template_data,
+                        repo_template_data,
+                        ignore_order=True).get(
+                        'values_changed',
+                        {})
+
+                    # If any changed values are found, push them to Intune
+                    if diff:
+                        diff_count += 1
+                        print(
+                            "Updating Message Template: " +
+                            mem_template_data['displayName'] +
+                            ", values changed:")
+                        values = get_diff_output(diff)
+                        for value in values:
+                            print(value)
+                        request_data = json.dumps(repo_template_data)
+                        q_param = None
+                        makeapirequestPatch(
+                            ENDPOINT +
+                            "/" +
+                            mem_template_data['id'],
+                            token,
+                            q_param,
+                            request_data)
+                    else:
+                        print("No difference found for Message Template: " +
+                              mem_template_data['displayName'])
+
+                    # Check each configured locale on the Notification Template
+                    # for changes
+                    for mem_locale, repo_locale in zip(
+                            mem_template_data['localizedNotificationMessages'],
+                            repo_data['localizedNotificationMessages']):
+                        del mem_locale['lastModifiedDateTime']
+                        repo_locale = remove_keys(repo_locale)
+
+                        diff = DeepDiff(
+                            mem_locale, repo_locale, ignore_order=True).get(
+                            'values_changed', {})
+
+                        # If any changed values are found, push them to Intune
                         if diff:
-                            print("Updating Message Template: " + \
-                                  mem_template_data['displayName'] + ", values changed:")
-                            print(*diff.items(), sep='\n')
-                            request_data = json.dumps(repo_template_data)
+                            diff_count += 1
+                            print(
+                                "Updating Message Template locale: " +
+                                mem_locale['locale'] +
+                                " for " +
+                                mem_template_data['displayName'] +
+                                ", values changed")
+                            values = get_diff_output(diff)
+                            for value in values:
+                                print(value)
+                            repo_locale.pop('isDefault', None)
+                            request_data = json.dumps(repo_locale)
                             q_param = None
-                            makeapirequestPatch(endpoint + "/" + mem_template_data['id'], token,q_param,request_data)
+                            makeapirequestPatch(
+                                ENDPOINT +
+                                "/" +
+                                mem_template_data['id'] +
+                                "/" +
+                                "localizedNotificationMessages" +
+                                "/" +
+                                mem_locale['id'],
+                                token,
+                                q_param,
+                                request_data)
                         else:
-                            print("No difference found for Message Template: " + \
-                                  mem_template_data['displayName'])
-
-                        ## Check each configured locale on the Notification Template for changes
-                        for mem_locale, repo_locale in zip(mem_template_data['localizedNotificationMessages'], repo_data['localizedNotificationMessages']):
-                            del mem_locale['lastModifiedDateTime']
-                            remove_keys = (
-                                'lastModifiedDateTime', 'id', 'locale')
-                            for k in remove_keys:
-                                repo_locale.pop(k, None)
-
-                            diff = DeepDiff(mem_locale, repo_locale, ignore_order=True).get('values_changed', {})
-
-                            ## If any changed values are found, push them to Intune
-                            if diff:
-                                print("Updating Message Template locale: " + \
-                                      mem_locale['locale'] + " for " + mem_template_data['displayName'] + ", values changed")
-                                for key, value in diff.items():
-                                    setting = re.search(
-                                        "\[(.*)\]", key).group(1)
-                                    new_val = value['new_value']
-                                    old_val = value['old_value']
-                                    print(
-                                        f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                repo_locale.pop('isDefault', None)
-                                request_data = json.dumps(repo_locale)
-                                q_param = None
-                                makeapirequestPatch(endpoint + "/" + mem_template_data['id'] + "/" + "localizedNotificationMessages" + "/" + mem_locale['id'], token,q_param,request_data)
-                            else:
-                                print(
-                                    "No difference in locale " + mem_locale['locale'] + " found for Message Teamplate: " + mem_template_data['displayName'])
-
-                    ## If Powershell script does not exist, create it and assign
-                    else:
-                        print("-" * 90)
-                        print(
-                            "Notification template not found, creating template: " + repo_data['displayName'])
-                        template = {
-                            "brandingOptions": repo_data['brandingOptions'],
-                            "displayName": repo_data['displayName'],
-                            "roleScopeTagIds": repo_data['roleScopeTagIds']
-                        }
-                        template_request_json = json.dumps(template)
-                        template_post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=template_request_json,status_code=200)
-                        for locale in repo_data['localizedNotificationMessages']:
-                            locale_request_json = json.dumps(locale)
-                            makeapirequestPost(endpoint + "/" + template_post_request['id'] + "/localizedNotificationMessages", token,q_param=None,jdata=locale_request_json,status_code=200)
-                        print("Notification template created with id: " + template_post_request['id'])
\ No newline at end of file
+                            print(
+                                "No difference in locale " +
+                                mem_locale['locale'] +
+                                " found for Message Template: " +
+                                mem_template_data['displayName'])
+
+                # If Notification Template does not exist, create it
+                else:
+                    print("-" * 90)
+                    print(
+                        "Notification template not found, creating template: " +
+                        repo_data['displayName'])
+                    template = {
+                        "brandingOptions": repo_data['brandingOptions'],
+                        "displayName": repo_data['displayName'],
+                        "roleScopeTagIds": repo_data['roleScopeTagIds']
+                    }
+                    template_request_json = json.dumps(template)
+                    template_post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=template_request_json, status_code=200)
+                    for locale in repo_data['localizedNotificationMessages']:
+                        locale_request_json = json.dumps(locale)
+                        makeapirequestPost(
+                            ENDPOINT +
+                            "/" +
+                            template_post_request['id'] +
+                            "/localizedNotificationMessages",
+                            token,
+                            q_param=None,
+                            jdata=locale_request_json,
+                            status_code=200)
+                    print(
+                        "Notification template created with id: " +
+                        template_post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_powershellScripts.py b/src/IntuneCD/update_powershellScripts.py
index b3296a51..b2d911f7 100644
--- a/src/IntuneCD/update_powershellScripts.py
+++ b/src/IntuneCD/update_powershellScripts.py
@@ -1,142 +1,170 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Powershell scripts in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all PowerShell scripts in Intune.
 """
 
 import json
 import os
 import base64
-import yaml
-import re
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from .graph_batch import batch_assignment, get_object_assignment
 from .update_assignment import update_assignment, post_assignment_update
-from deepdiff import DeepDiff
+from .check_file import check_file
+from .load_file import load_file
+from .remove_keys import remove_keys
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
 
 
 def update(path, token, assignment=False):
-
-    ## Set Powershell script path
-    configpath = path+"/"+"Scripts/Powershell"
-    ## If Powershell script path exists, continue
-    if os.path.exists(configpath) == True:
-        ## Get scripts
-        mem_powershellScript = makeapirequest(endpoint, token)
-        ## Get current assignment
-        mem_assignments = batch_assignment(mem_powershellScript,'deviceManagement/deviceManagementScripts/','/assignments',token)
+    """
+    This function updates all Powershell scripts 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
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set Powershell script path
+    configpath = path + "/" + "Scripts/Powershell"
+    # If Powershell script path exists, continue
+    if os.path.exists(configpath):
+        # Get scripts
+        mem_powershellScript = makeapirequest(ENDPOINT, token)
+        # Get current assignment
+        mem_assignments = batch_assignment(
+            mem_powershellScript,
+            'deviceManagement/deviceManagementScripts/',
+            '/assignments',
+            token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
+            file = check_file(configpath, filename)
+            if file is False:
                 continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
-                continue
-
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                        
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    data = {'value':''}
-                    if mem_powershellScript['value']:
-                        for val in mem_powershellScript['value']:
-                            if repo_data['displayName'] == val['displayName']:
-                                data['value'] = val
-
-                    ## If Powershell script exists, continue
-                    if data['value']:
-                        print("-" * 90)
-                        ## Get Powershell script details
-                        mem_data = makeapirequest(endpoint + "/" + data['value']['id'], token)
-                        mem_id = mem_data['id']
-                        ## Remove keys before using DeepDiff
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime'}
-                        for k in remove_keys:
-                            mem_data.pop(k, None)
-
-                        ## Check if script data is saved and read the file
-                        if os.path.exists(configpath + "/Script Data/" + repo_data['fileName']):
-                            with open(configpath + "/Script Data/" + repo_data['fileName'], 'r') as f:
-                                repo_payload_config = f.read()
-
-                            mem_payload_config = base64.b64decode(
-                                mem_data['scriptContent']).decode('utf-8')
-
-                            pdiff = DeepDiff(mem_payload_config, repo_payload_config, ignore_order=True).get('values_changed', {})
-                            cdiff = DeepDiff(mem_data, repo_data, ignore_order=True, exclude_paths="root['scriptContent']").get('values_changed', {})
-
-                            ## If any changed values are found, push them to Intune
-                            if pdiff or cdiff:
-                                print("Updating Powershell script: " + repo_data['displayName'] + ", values changed:")
-                                if cdiff:
-                                    for key, value in cdiff.items():
-                                        setting = re.search(
-                                            "\[(.*)\]", key).group(1)
-                                        new_val = value['new_value']
-                                        old_val = value['old_value']
-                                        print(
-                                            f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                if pdiff:
-                                    print(
-                                        "Script changed, check commit history for change details")
-                                powershell_bytes = repo_payload_config.encode(
-                                    'utf-8')
-                                repo_data['scriptContent'] = base64.b64encode(
-                                    powershell_bytes).decode('utf-8')
-                                request_data = json.dumps(repo_data)
-                                q_param = None
-                                makeapirequestPatch(endpoint + "/" + mem_id, token,q_param,request_data)
-                            else:
+                repo_data = load_file(filename, f)
+
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                data = {'value': ''}
+                if mem_powershellScript['value']:
+                    for val in mem_powershellScript['value']:
+                        if repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                # If Powershell script exists, continue
+                if data['value']:
+                    print("-" * 90)
+                    # Get Powershell script details
+                    mem_data = makeapirequest(
+                        ENDPOINT + "/" + data['value']['id'], token)
+                    mem_id = mem_data['id']
+                    # Remove keys before using DeepDiff
+                    mem_data = remove_keys(mem_data)
+
+                    # Check if script data is saved and read the file
+                    if os.path.exists(
+                        configpath +
+                        "/Script Data/" +
+                            repo_data['fileName']):
+                        with open(configpath + "/Script Data/" + repo_data['fileName'], 'r') as f:
+                            repo_payload_config = f.read()
+
+                        mem_payload_config = base64.b64decode(
+                            mem_data['scriptContent']).decode('utf-8')
+
+                        pdiff = DeepDiff(
+                            mem_payload_config,
+                            repo_payload_config,
+                            ignore_order=True).get(
+                            'values_changed',
+                            {})
+                        cdiff = DeepDiff(
+                            mem_data,
+                            repo_data,
+                            ignore_order=True,
+                            exclude_paths="root['scriptContent']").get(
+                            'values_changed',
+                            {})
+
+                        # If any changed values are found, push them to Intune
+                        if pdiff or cdiff:
+                            print(
+                                "Updating Powershell script: " +
+                                repo_data['displayName'] +
+                                ", values changed:")
+                            if cdiff:
+                                diff_count += 1
+                                values = get_diff_output(cdiff)
+                                for value in values:
+                                    print(value)
+                            if pdiff:
+                                diff_count += 1
                                 print(
-                                    'No difference found for Powershell script: ' + repo_data['displayName'])
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_id,mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['deviceManagementScriptAssignments'] = update
-                                post_assignment_update(request_data,mem_id,'deviceManagement/deviceManagementScripts','assign',token)
-
-                    ## If Powershell script does not exist, create it and assign
-                    else:
-                        print("-" * 90)
-                        print(
-                            "Powershell script not found, creating script: " + repo_data['displayName'])
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=request_json,status_code=201)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
-                            request_data = {}
-                            request_data['deviceManagementScriptAssignments'] = assignment
-                            post_assignment_update(request_data,post_request['id'],'deviceManagement/deviceManagementScripts','assign',token)
-                        print("Powershell script created with id: " + post_request['id'])
\ No newline at end of file
+                                    "Script changed, check commit history for change details")
+                            powershell_bytes = repo_payload_config.encode(
+                                'utf-8')
+                            repo_data['scriptContent'] = base64.b64encode(
+                                powershell_bytes).decode('utf-8')
+                            request_data = json.dumps(repo_data)
+                            q_param = None
+                            makeapirequestPatch(
+                                ENDPOINT + "/" + mem_id, token, q_param, request_data)
+                        else:
+                            print(
+                                'No difference found for Powershell script: ' +
+                                repo_data['displayName'])
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_id, mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {'deviceManagementScriptAssignments': update}
+                            post_assignment_update(
+                                request_data,
+                                mem_id,
+                                'deviceManagement/deviceManagementScripts',
+                                'assign',
+                                token)
+
+                # If Powershell script does not exist, create it and assign
+                else:
+                    print("-" * 90)
+                    print(
+                        "Powershell script not found, creating script: " +
+                        repo_data['displayName'])
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {'deviceManagementScriptAssignments': assignment}
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            'deviceManagement/deviceManagementScripts',
+                            'assign',
+                            token)
+                    print(
+                        "Powershell script created with id: " +
+                        post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_proactiveRemediation.py b/src/IntuneCD/update_proactiveRemediation.py
index ad36acfa..378dbb3b 100644
--- a/src/IntuneCD/update_proactiveRemediation.py
+++ b/src/IntuneCD/update_proactiveRemediation.py
@@ -1,157 +1,187 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Powershell scripts in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Proactive Remediation in Intune.
 """
 
 import json
 import os
 import base64
-import yaml
-import re
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from .graph_batch import batch_assignment, get_object_assignment
 from .update_assignment import update_assignment, post_assignment_update
-from deepdiff import DeepDiff
+from .check_file import check_file
+from .load_file import load_file
+from .remove_keys import remove_keys
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts"
 
 
 def update(path, token, assignment=False):
+    """
+    This function updates all Proactive Remediation in Intune if,
+    the configuration in Intune differs from the JSON/YAML file.
 
-    ## Set Powershell script path
+    :param path: Path to where the backup is saved
+    :param token: Token to use for authenticating the request
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set Powershell script path
     configpath = f'{path}/Proactive Remediations'
-    ## If Powershell script path exists, continue
-    if os.path.exists(configpath) == True:
-        ## Get Proactive remediations
-        mem_proactiveRemediation = makeapirequest(endpoint, token)
-        ## Get current assignment
-        mem_assignments = batch_assignment(mem_proactiveRemediation,'deviceManagement/deviceHealthScripts/','/assignments',token)
+    # If Powershell script path exists, continue
+    if os.path.exists(configpath):
+        # Get Proactive remediation's
+        mem_proactiveRemediation = makeapirequest(ENDPOINT, token)
+        # Get current assignment
+        mem_assignments = batch_assignment(
+            mem_proactiveRemediation,
+            'deviceManagement/deviceHealthScripts/',
+            '/assignments',
+            token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
-                continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
+            file = check_file(configpath, filename)
+            if file is False:
                 continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
-                continue
-
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                        
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    data = {'value':''}
-                    if mem_proactiveRemediation['value']:
-                        for val in mem_proactiveRemediation['value']:
-                            if repo_data['displayName'] == val['displayName']:
-                                data['value'] = val
-
-                    ## If Powershell script exists, continue
-                    if data['value']:
-                        print("-" * 90)
-                        q_param = None
-                        ## Get Powershell script details
-                        mem_data = makeapirequest(endpoint + "/" + data['value']['id'], token,q_param)
-                        mem_id = data['value']['id']
-                        ## Remove keys before using DeepDiff
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime','isGlobalScript','highestAvailableVersion'}
-                        for k in remove_keys:
-                            mem_data.pop(k, None)
-
-                        ## Check if script data is saved and read the file
-                        detection_script_name = f"{configpath}/Script Data/{repo_data['displayName']}_DetectionScript.ps1"
-                        remediation_script_name = f"{configpath}/Script Data/{repo_data['displayName']}_RemediationScript.ps1"
-                        if os.path.exists(detection_script_name) and os.path.exists(remediation_script_name):
-                            with open(detection_script_name, 'r') as df:
-                                repo_detection_config = df.read()
-                            with open(remediation_script_name, 'r') as rf:
-                                repo_remediation_config = rf.read()
-
-                            mem_detection_config = base64.b64decode(
-                                mem_data['detectionScriptContent']).decode('utf-8')
-                            mem_remediation_config = base64.b64decode(
-                                mem_data['remediationScriptContent']).decode('utf-8')
-
-                            ddiff = DeepDiff(mem_detection_config, repo_detection_config, ignore_order=True).get('values_changed', {})
-                            rdiff = DeepDiff(mem_remediation_config, repo_remediation_config, ignore_order=True).get('values_changed', {})
-                            cdiff = DeepDiff(mem_data, repo_data, ignore_order=True, exclude_paths=["root['detectionScriptContent']",
-                                                                                                    "root['remediationScriptContent']"]).get('values_changed', {})
-
-                            ## If any changed values are found, push them to Intune
-                            if cdiff or ddiff or rdiff:
-                                print("Updating Proactive Remediation: " + \
-                                      repo_data['displayName'] + ", values changed:")
-                                if cdiff:
-                                    for key, value in cdiff.items():
-                                        setting = re.search("\[(.*)\]", key).group(1)
-                                        new_val = value['new_value']
-                                        old_val = value['old_value']
-                                        print(f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                if ddiff:
-                                    print(
-                                        "Detection script changed, check commit history for change details")
-                                if rdiff:
-                                    print(
-                                        "Remediation script changed, check commit history for change details")
-                                detection_bytes = repo_detection_config.encode(
-                                    'utf-8')
-                                remediation_bytes = repo_remediation_config.encode(
-                                    'utf-8')
-                                repo_data['detectionScriptContent'] = base64.b64encode(
-                                    detection_bytes).decode('utf-8')
-                                repo_data['remediationScriptContent'] = base64.b64encode(
-                                    remediation_bytes).decode('utf-8')
-                                request_data = json.dumps(repo_data)
-                                q_param = None
-                                makeapirequestPatch(endpoint + "/" + mem_id, token,q_param,request_data)
-                            else:
+                repo_data = load_file(filename, f)
+
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                data = {'value': ''}
+                if mem_proactiveRemediation['value']:
+                    for val in mem_proactiveRemediation['value']:
+                        if repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                # If Powershell script exists, continue
+                if data['value']:
+                    print("-" * 90)
+                    q_param = None
+                    # Get Powershell script details
+                    mem_data = makeapirequest(
+                        ENDPOINT + "/" + data['value']['id'], token, q_param)
+                    mem_id = data['value']['id']
+                    # Remove keys before using DeepDiff
+                    mem_data = remove_keys(mem_data)
+
+                    # Check if script data is saved and read the file
+                    detection_script_name = f"{configpath}/Script Data/{repo_data['displayName']}_DetectionScript.ps1"
+                    remediation_script_name = f"{configpath}/Script Data/{repo_data['displayName']}_RemediationScript.ps1"
+                    if os.path.exists(detection_script_name) and os.path.exists(
+                            remediation_script_name):
+                        with open(detection_script_name, 'r') as df:
+                            repo_detection_config = df.read()
+                        with open(remediation_script_name, 'r') as rf:
+                            repo_remediation_config = rf.read()
+
+                        mem_detection_config = base64.b64decode(
+                            mem_data['detectionScriptContent']).decode('utf-8')
+                        mem_remediation_config = base64.b64decode(
+                            mem_data['remediationScriptContent']).decode('utf-8')
+
+                        ddiff = DeepDiff(
+                            mem_detection_config,
+                            repo_detection_config,
+                            ignore_order=True).get(
+                            'values_changed',
+                            {})
+                        rdiff = DeepDiff(
+                            mem_remediation_config,
+                            repo_remediation_config,
+                            ignore_order=True).get(
+                            'values_changed',
+                            {})
+                        cdiff = DeepDiff(
+                            mem_data,
+                            repo_data,
+                            ignore_order=True,
+                            exclude_paths=[
+                                "root['detectionScriptContent']",
+                                "root['remediationScriptContent']"]).get(
+                            'values_changed',
+                            {})
+
+                        # If any changed values are found, push them to Intune
+                        if cdiff or ddiff or rdiff:
+                            print(
+                                "Updating Proactive Remediation: " +
+                                repo_data['displayName'] +
+                                ", values changed:")
+                            if cdiff:
+                                diff_count += 1
+                                values = get_diff_output(cdiff)
+                                for value in values:
+                                    print(value)
+                            if ddiff:
+                                diff_count += 1
+                                print(
+                                    "Detection script changed, check commit history for change details")
+                            if rdiff:
+                                diff_count += 1
                                 print(
-                                    'No difference found for Proactive Remediation: ' + repo_data['displayName'])
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_id,mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['deviceHealthScriptAssignments'] = update
-                                post_assignment_update(request_data,mem_id,'deviceManagement/deviceHealthScripts','assign',token)    
-
-                    ## If Powershell script does not exist, create it and assign
-                    else:
-                        print("-" * 90)
-                        print("Proactive Remediation not found, creating: " + \
-                              repo_data['displayName'])
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=request_json,status_code=201)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
-                            request_data = {}
-                            request_data['deviceHealthScriptAssignments'] = assignment
-                            post_assignment_update(request_data,post_request['id'],'deviceManagement/deviceHealthScripts','assign',token)
-                        print("Proactive Remediation created with id: " + post_request['id'])
\ No newline at end of file
+                                    "Remediation script changed, check commit history for change details")
+                            detection_bytes = repo_detection_config.encode(
+                                'utf-8')
+                            remediation_bytes = repo_remediation_config.encode(
+                                'utf-8')
+                            repo_data['detectionScriptContent'] = base64.b64encode(
+                                detection_bytes).decode('utf-8')
+                            repo_data['remediationScriptContent'] = base64.b64encode(
+                                remediation_bytes).decode('utf-8')
+                            request_data = json.dumps(repo_data)
+                            q_param = None
+                            makeapirequestPatch(
+                                ENDPOINT + "/" + mem_id, token, q_param, request_data)
+                        else:
+                            print(
+                                'No difference found for Proactive Remediation: ' +
+                                repo_data['displayName'])
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_id, mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {'deviceHealthScriptAssignments': update}
+                            post_assignment_update(
+                                request_data, mem_id, 'deviceManagement/deviceHealthScripts', 'assign', token)
+
+                            # If Powershell script does not exist, create it
+                            # and assign
+                else:
+                    print("-" * 90)
+                    print("Proactive Remediation not found, creating: " +
+                          repo_data['displayName'])
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {'deviceHealthScriptAssignments': assignment}
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            'deviceManagement/deviceHealthScripts',
+                            'assign',
+                            token)
+                    print(
+                        "Proactive Remediation created with id: " +
+                        post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_profiles.py b/src/IntuneCD/update_profiles.py
index ee579a81..8b7dcc1d 100644
--- a/src/IntuneCD/update_profiles.py
+++ b/src/IntuneCD/update_profiles.py
@@ -1,260 +1,294 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Device Configurations in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Device Configuration Profiles in Intune.
 """
 
 import json
 import os
 import base64
-import yaml
 import plistlib
-import re
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from .graph_batch import batch_assignment, get_object_assignment
 from .update_assignment import update_assignment, post_assignment_update
-from deepdiff import DeepDiff
+from .check_file import check_file
+from .load_file import load_file
+from .remove_keys import remove_keys
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations"
 
 
 def update(path, token, assignment=False):
+    """
+    This function updates all Device Configurations in Intune,
+    if the configuration in Intune differs from the JSON/YAML file.
 
-    ## Set Device Configurations path
-    configpath = path+"/"+"Device Configurations/"
-    ## If Device Configurations path exists, continue
-    if os.path.exists(configpath) == True:
-        ## Get profiles
-        mem_data = makeapirequest(endpoint, token)
-        ## Get current assignment
-        mem_assignments = batch_assignment(mem_data,'deviceManagement/deviceConfigurations/','/assignments',token)
-        
-        for filename in os.listdir(configpath):
+    :param path: Path to where the backup is saved
+    :param token: Token to use for authenticating the request
+    :param assignment: Boolean to determine if assignments should be updated
+    """
 
-            file = os.path.join(configpath, filename)
-            ## If path is Directory, skip
-            if os.path.isdir(file):
-                continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
-                continue
+    diff_count = 0
+    # Set Device Configurations path
+    configpath = path + "/" + "Device Configurations/"
+    # If Device Configurations path exists, continue
+    if os.path.exists(configpath):
+        # Get profiles
+        mem_data = makeapirequest(ENDPOINT, token)
+        # Get current assignment
+        mem_assignments = batch_assignment(
+            mem_data,
+            'deviceManagement/deviceConfigurations/',
+            '/assignments',
+            token)
 
-            ## Check which format the file is saved as then open file, load data and set query parameter
+        for filename in os.listdir(configpath):
+            file = check_file(configpath, filename)
+            if file is False:
+                continue
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                        
-                    elif filename.endswith(".json"):
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    ## If Device Configuration exists, continue
-                    data = {'value':''}
-                    if mem_data['value']:
-                        ## Updating Windows update rings is currently not supported
-                        if repo_data['@odata.type'] == "#microsoft.graph.windowsUpdateForBusinessConfiguration":
-                            continue
-
-                        for val in mem_data['value']:
-                            if repo_data['@odata.type'] == val['@odata.type'] and \
-                               repo_data['displayName'] == val['displayName']:
-                                data['value'] = val
-                                
-                    if data['value']:
-
-                        print("-" * 90)
-                        mem_id = data['value']['id']
-                        ## Remove keys before using DeepDiff
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime'}
-                        for k in remove_keys:
-                            data['value'].pop(k, None)
-
-                        ## If Device Condifguration is custom macOS or iOS, compare the .mobileconfig
-                        if ((repo_data['@odata.type'] == "#microsoft.graph.macOSCustomConfiguration") or (repo_data['@odata.type'] == "#microsoft.graph.iosCustomConfiguration")):
-                            if os.path.exists(configpath + "mobileconfig/" + repo_data['payloadFileName']):
-                                with open(configpath + "mobileconfig/" + repo_data['payloadFileName'], 'rb') as f:
-                                    repo_payload_config = plistlib.load(f)
-
-                                decoded = base64.b64decode(
-                                    data['value']['payload']).decode('utf-8')
-                                f = open(configpath + 'temp.mobileconfig', 'w')
-                                f.write(decoded)
-                                with open(configpath + 'temp.mobileconfig', 'rb') as f:
-                                    mem_payload_config = plistlib.load(f)
-
-                                pdiff = DeepDiff(mem_payload_config, repo_payload_config, ignore_order=True).get('values_changed', {})
-                                cdiff = DeepDiff(data['value'], repo_data, ignore_order=True, exclude_paths="root['payload']").get('values_changed', {})
-
-                                ## If any changed values are found, push them to Intune
-                                if pdiff or cdiff:
-                                    print(
-                                        "Updating profile: " + repo_data['displayName'] + ", values changed:")
-                                    if pdiff:
-                                        for key, value in pdiff.items():
-                                            setting = re.search(
-                                                "\[(.*)\]", key).group(1).split("[")[-1]
-                                            new_val = value['new_value']
-                                            old_val = value['old_value']
-                                            print(
-                                                f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                    if cdiff:
-                                        for key, value in cdiff.items():
-                                            setting = re.search(
-                                                "\[(.*)\]", key).group(1)
-                                            new_val = value['new_value']
-                                            old_val = value['old_value']
-                                            print(
-                                                f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                    payload = plistlib.dumps(
-                                        repo_payload_config)
-                                    repo_data['payload'] = str(base64.b64encode(payload), 'utf-8')
-                                    request_data = json.dumps(repo_data)
-                                    q_param = None
-                                    makeapirequestPatch(endpoint + "/" + mem_id, token,q_param,request_data,status_code=204)
-                                else:
-                                    print(
-                                        'No difference found for profile: ' + repo_data['displayName'])
+                repo_data = load_file(filename, f)
 
-                                os.remove(configpath + 'temp.mobileconfig')
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
 
-                            else:
-                                print("No mobileconfig found for profile: " + \
-                                    repo_data['displayName'])
+                # If Device Configuration exists, continue
+                data = {'value': ''}
+                if mem_data['value']:
+                    # Updating Windows update rings is currently not supported
+                    if repo_data['@odata.type'] == "#microsoft.graph.windowsUpdateForBusinessConfiguration":
+                        continue
 
-                        ## If Device Configuration is custom Win10, compare the OMA settings
-                        elif data['value']['@odata.type'] == "#microsoft.graph.windows10CustomConfiguration":
-                            print("Checking if Win10 Custom Profile: " + \
-                                repo_data['displayName'] + " has any upates")
-                            omas = []
-                            for setting in data['value']['omaSettings']:
-                                if setting['isEncrypted'] == True:
-                                    decoded_oma = {}
-                                    oma_value = makeapirequest(endpoint + "/" + mem_id + "/getOmaSettingPlainTextValue(secretReferenceValueId='" + setting['secretReferenceValueId'] + "')", token)
-                                    decoded_oma['@odata.type'] = setting['@odata.type']
-                                    decoded_oma['displayName'] = setting['displayName']
-                                    decoded_oma['description'] = setting['description']
-                                    decoded_oma['omaUri'] = setting['omaUri']
-                                    decoded_oma['value'] = oma_value
-                                    decoded_oma['isEncrypted'] = True
-                                    decoded_oma['secretReferenceValueId'] = None
-                                    omas.append(decoded_oma)
-                                elif setting['isEncrypted'] == False:
-                                    omas.append(setting)
-
-                            data['value'].pop('omaSettings')
-                            data['value']['omaSettings'] = omas
-
-                            repo_omas = []
-                            for mem_omaSetting, repo_omaSetting in zip(data['value']['omaSettings'], repo_data['omaSettings']):
-
-                                diff = DeepDiff(mem_omaSetting, repo_omaSetting, ignore_order=True, exclude_paths="root['isEncrypted']").get('values_changed', {})
-
-                                ## If any changed values are found, push them to Intune
-                                if diff:
-                                    print(
-                                        "Updating oma setting: " + repo_omaSetting['omaUri'] + ", values changed:")
-                                    for key, value in diff.items():
-                                        new_val = value['new_value']
-                                        old_val = value['old_value']
-                                        print(
-                                            f"New Value: {new_val}, Old Value: {old_val}")
-                                    if type(repo_omaSetting['value']) is dict:
-                                        remove_keys = {'isReadOnly', 'secretReferenceValueId','isEncrypted'}
-                                        for k in remove_keys:
-                                            repo_omaSetting.pop(k, None)
-                                        repo_omaSetting['value'] = repo_omaSetting['value']['value']
-                                        repo_omas.append(repo_omaSetting)
-                                    else:
-                                        remove_keys = {'isReadOnly', 'secretReferenceValueId','isEncrypted'}
-                                        for k in remove_keys:
-                                            repo_omaSetting.pop(k, None)
-                                        repo_omas.append(repo_omaSetting)
-
-                            repo_data.pop('omaSettings')
-                            repo_data['omaSettings'] = repo_omas
-
-                            if repo_omas:
+                    for val in mem_data['value']:
+                        if repo_data['@odata.type'] == val['@odata.type'] and \
+                                repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                if data['value']:
+
+                    print("-" * 90)
+                    mem_id = data['value']['id']
+                    # Remove keys before using DeepDiff
+                    data['value'] = remove_keys(data['value'])
+
+                    # If Device Configuration is custom macOS or iOS, compare
+                    # the .mobileconfig
+                    if ((repo_data['@odata.type'] == "#microsoft.graph.macOSCustomConfiguration") or (
+                            repo_data['@odata.type'] == "#microsoft.graph.iosCustomConfiguration")):
+                        if os.path.exists(
+                            configpath +
+                            "mobileconfig/" +
+                                repo_data['payloadFileName']):
+                            with open(configpath + "mobileconfig/" + repo_data['payloadFileName'], 'rb') as f:
+                                repo_payload_config = plistlib.load(f)
+
+                            decoded = base64.b64decode(
+                                data['value']['payload']).decode('utf-8')
+                            f = open(configpath + 'temp.mobileconfig', 'w')
+                            f.write(decoded)
+                            with open(configpath + 'temp.mobileconfig', 'rb') as f:
+                                mem_payload_config = plistlib.load(f)
+
+                            pdiff = DeepDiff(
+                                mem_payload_config,
+                                repo_payload_config,
+                                ignore_order=True).get(
+                                'values_changed',
+                                {})
+                            cdiff = DeepDiff(
+                                data['value'],
+                                repo_data,
+                                ignore_order=True,
+                                exclude_paths="root['payload']").get(
+                                'values_changed',
+                                {})
+
+                            # If any changed values are found, push them to
+                            # Intune
+                            if pdiff or cdiff:
+                                print(
+                                    "Updating profile: " +
+                                    repo_data['displayName'] +
+                                    ", values changed:")
+                                if pdiff:
+                                    diff_count += 1
+                                    values = get_diff_output(pdiff)
+                                    for value in values:
+                                        print(value)
+                                if cdiff:
+                                    diff_count += 1
+                                    values = get_diff_output(cdiff)
+                                    for value in values:
+                                        print(value)
+                                payload = plistlib.dumps(
+                                    repo_payload_config)
+                                repo_data['payload'] = str(
+                                    base64.b64encode(payload), 'utf-8')
                                 request_data = json.dumps(repo_data)
                                 q_param = None
-                                makeapirequestPatch(endpoint + "/" + mem_id, token,q_param,request_data,status_code=204)
+                                makeapirequestPatch(
+                                    ENDPOINT + "/" + mem_id, token, q_param, request_data, status_code=204)
+                            else:
+                                print(
+                                    'No difference found for profile: ' +
+                                    repo_data['displayName'])
+
+                            os.remove(configpath + 'temp.mobileconfig')
 
-                        ## If Device Configuration is not custom, compare the values
                         else:
-                            diff = DeepDiff(data['value'], repo_data, ignore_order=True).get('values_changed', {})
+                            print("No mobileconfig found for profile: " +
+                                  repo_data['displayName'])
+
+                    # If Device Configuration is custom Win10, compare the OMA
+                    # settings
+                    elif data['value']['@odata.type'] == "#microsoft.graph.windows10CustomConfiguration":
+                        print("Checking if Win10 Custom Profile: " +
+                              repo_data['displayName'] + " has any upates")
+                        omas = []
+                        for setting in data['value']['omaSettings']:
+                            if setting['isEncrypted']:
+                                decoded_oma = {}
+                                oma_value = makeapirequest(
+                                    ENDPOINT +
+                                    "/" +
+                                    mem_id +
+                                    "/getOmaSettingPlainTextValue(secretReferenceValueId='" +
+                                    setting['secretReferenceValueId'] +
+                                    "')",
+                                    token)
+                                decoded_oma['@odata.type'] = setting['@odata.type']
+                                decoded_oma['displayName'] = setting['displayName']
+                                decoded_oma['description'] = setting['description']
+                                decoded_oma['omaUri'] = setting['omaUri']
+                                decoded_oma['value'] = oma_value
+                                decoded_oma['isEncrypted'] = True
+                                decoded_oma['secretReferenceValueId'] = None
+                                omas.append(decoded_oma)
+                            elif not setting['isEncrypted']:
+                                omas.append(setting)
+
+                        data['value'].pop('omaSettings')
+                        data['value']['omaSettings'] = omas
+
+                        repo_omas = []
+                        for mem_omaSetting, repo_omaSetting in zip(
+                                data['value']['omaSettings'], repo_data['omaSettings']):
+
+                            diff = DeepDiff(
+                                mem_omaSetting,
+                                repo_omaSetting,
+                                ignore_order=True,
+                                exclude_paths="root['isEncrypted']").get(
+                                'values_changed',
+                                {})
 
-                            ## If any changed values are found, push them to Intune
+                            # If any changed values are found, push them to
+                            # Intune
                             if diff:
+                                diff_count += 1
                                 print(
-                                    "Updating profile: " + repo_data['displayName'] + ", values changed:")
+                                    "Updating oma setting: " +
+                                    repo_omaSetting['omaUri'] +
+                                    ", values changed:")
                                 for key, value in diff.items():
-                                    setting = re.search(
-                                        "\[(.*)\]", key).group(1)
                                     new_val = value['new_value']
                                     old_val = value['old_value']
                                     print(
-                                        f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                request_data = json.dumps(repo_data)
-                                q_param = None
-                                makeapirequestPatch(endpoint + "/" + mem_id, token,q_param,request_data,status_code=204)
-                            else:
-                                print('No difference found for profile: ' + \
-                                    repo_data['displayName'])
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_id,mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['assignments'] = update
-                                post_assignment_update(request_data,mem_id,'deviceManagement/deviceConfigurations','assign',token)
-
-                    ## If profile does not exist, create it and assign
-                    else:
-                        ## If profile is custom win10, create correct omaSettings format before posting
-                        if repo_data['@odata.type'] == "#microsoft.graph.windows10CustomConfiguration":
-                            repo_omas = []
-                            for repo_omaSetting in repo_data['omaSettings']:
+                                        f"New Value: {new_val}, Old Value: {old_val}")
                                 if type(repo_omaSetting['value']) is dict:
-                                    remove_keys = {'isReadOnly', 'secretReferenceValueId','isEncrypted'}
-                                    for k in remove_keys:
-                                        repo_omaSetting.pop(k, None)
+                                    repo_omaSetting = remove_keys(
+                                        repo_omaSetting)
                                     repo_omaSetting['value'] = repo_omaSetting['value']['value']
                                     repo_omas.append(repo_omaSetting)
                                 else:
-                                    remove_keys = {'isReadOnly', 'secretReferenceValueId','isEncrypted'}
-                                    for k in remove_keys:
-                                        repo_omaSetting.pop(k, None)
+                                    repo_omaSetting = remove_keys(
+                                        repo_omaSetting)
                                     repo_omas.append(repo_omaSetting)
-                            repo_data.pop('omaSettings')
-                            repo_data['omaSettings'] = repo_omas
-                        ## Post new profile
-                        print("-" * 90)
-                        print("Profile not found, creating profile: " + \
-                              repo_data['displayName'])
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=request_json,status_code=201)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
-                            request_data = {}
-                            request_data['assignments'] = assignment
-                            post_assignment_update(request_data,post_request['id'],'deviceManagement/deviceConfigurations','assign',token)
-                        print("Profile created with id: " + post_request['id'])
\ No newline at end of file
+
+                        repo_data.pop('omaSettings')
+                        repo_data['omaSettings'] = repo_omas
+
+                        if repo_omas:
+                            request_data = json.dumps(repo_data)
+                            q_param = None
+                            makeapirequestPatch(
+                                ENDPOINT + "/" + mem_id, token, q_param, request_data, status_code=204)
+
+                    # If Device Configuration is not custom, compare the values
+                    else:
+                        diff = DeepDiff(
+                            data['value'], repo_data, ignore_order=True).get(
+                            'values_changed', {})
+
+                        # If any changed values are found, push them to Intune
+                        if diff:
+                            diff_count += 1
+                            print(
+                                "Updating profile: " +
+                                repo_data['displayName'] +
+                                ", values changed:")
+                            values = get_diff_output(diff)
+                            for value in values:
+                                print(value)
+                            request_data = json.dumps(repo_data)
+                            q_param = None
+                            makeapirequestPatch(
+                                ENDPOINT + "/" + mem_id, token, q_param, request_data, status_code=204)
+                        else:
+                            print('No difference found for profile: ' +
+                                  repo_data['displayName'])
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_id, mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {'assignments': update}
+                            post_assignment_update(
+                                request_data, mem_id, 'deviceManagement/deviceConfigurations', 'assign', token)
+
+                # If profile does not exist, create it and assign
+                else:
+                    # If profile is custom win10, create correct omaSettings
+                    # format before posting
+                    if repo_data['@odata.type'] == "#microsoft.graph.windows10CustomConfiguration":
+                        repo_omas = []
+                        for repo_omaSetting in repo_data['omaSettings']:
+                            if type(repo_omaSetting['value']) is dict:
+                                repo_omaSetting = remove_keys(repo_omaSetting)
+                                repo_omaSetting['value'] = repo_omaSetting['value']['value']
+                                repo_omas.append(repo_omaSetting)
+                            else:
+                                repo_omaSetting = remove_keys(repo_omaSetting)
+                                repo_omas.append(repo_omaSetting)
+                        repo_data.pop('omaSettings')
+                        repo_data['omaSettings'] = repo_omas
+                    # Post new profile
+                    print("-" * 90)
+                    print("Profile not found, creating profile: " +
+                          repo_data['displayName'])
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {'assignments': assignment}
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            'deviceManagement/deviceConfigurations',
+                            'assign',
+                            token)
+                    print("Profile created with id: " + post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_shellScripts.py b/src/IntuneCD/update_shellScripts.py
index 3d5b3649..42efbe63 100644
--- a/src/IntuneCD/update_shellScripts.py
+++ b/src/IntuneCD/update_shellScripts.py
@@ -1,143 +1,170 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Shell scripts in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Shell Scripts in Intune.
 """
 
 import json
 import os
 import base64
-import yaml
-import re
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from .graph_batch import batch_assignment, get_object_assignment
-from .update_assignment import update_assignment,post_assignment_update
-from deepdiff import DeepDiff
+from .update_assignment import update_assignment, post_assignment_update
+from .check_file import check_file
+from .load_file import load_file
+from .remove_keys import remove_keys
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceShellScripts"
-assignment_endpoint = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceShellScripts"
+ASSIGNMENT_ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
 
-def update(path, token, assignment=False):
 
-    ## Set Shell scritp path
-    configpath = path+"/"+"Scripts/Shell"
-    ## If Shell script path exists, continue
-    if os.path.exists(configpath) == True:
-        ## Get scripts
-        mem_shellScript = makeapirequest(endpoint, token)
-        ## Get current assignment
-        mem_assignments = batch_assignment(mem_shellScript,'deviceManagement/deviceManagementScripts/','/assignments',token)
+def update(path, token, assignment=False):
+    """
+    This function updates all Shell scripts 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
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set Shell scritp path
+    configpath = path + "/" + "Scripts/Shell"
+    # If Shell script path exists, continue
+    if os.path.exists(configpath):
+        # Get scripts
+        mem_shellScript = makeapirequest(ENDPOINT, token)
+        # Get current assignment
+        mem_assignments = batch_assignment(
+            mem_shellScript,
+            'deviceManagement/deviceManagementScripts/',
+            '/assignments',
+            token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
+            file = check_file(configpath, filename)
+            if file is False:
                 continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
-                continue
-
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                        
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    data = {'value':''}
-                    if mem_shellScript['value']:
-                        for val in mem_shellScript['value']:
-                            if repo_data['displayName'] == val['displayName']:
-                                data['value'] = val
-
-                    ## If Shell script exists, continue
-                    if data['value']:
-                        print("-" * 90)
-                        q_param = None
-                        ## Get Shell script details
-                        mem_data = makeapirequest(endpoint + "/" + data['value']['id'], token)
-                        mem_id = mem_data['id']
-                        ## Remove keys before using DeepDiff
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime'}
-                        for k in remove_keys:
-                            mem_data.pop(k, None)
-
-                        ## Check if script data is saved and read the file
-                        if os.path.exists(configpath + "/Script Data/" + repo_data['fileName']):
-                            with open(configpath + "/Script Data/" + repo_data['fileName'], 'r') as f:
-                                repo_payload_config = f.read()
-
-                            mem_payload_config = base64.b64decode(
-                                mem_data['scriptContent']).decode('utf-8')
-
-                            pdiff = DeepDiff(mem_payload_config, repo_payload_config, ignore_order=True).get('values_changed', {})
-                            cdiff = DeepDiff(mem_data, repo_data, ignore_order=True, exclude_paths="root['scriptContent']").get('values_changed', {})
-
-                            ## If any changed values are found, push them to Intune
-                            if pdiff or cdiff:
-                                print("Updating Shell script: " + repo_data['displayName'] + ", values changed:")
-                                if cdiff:
-                                    for key, value in cdiff.items():
-                                        setting = re.search(
-                                            "\[(.*)\]", key).group(1)
-                                        new_val = value['new_value']
-                                        old_val = value['old_value']
-                                        print(
-                                            f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                                if pdiff:
-                                    print(
-                                        "Script changed, check commit history for change details")
-                                shell_bytes = repo_payload_config.encode(
-                                    'utf-8')
-                                repo_data['scriptContent'] = base64.b64encode(
-                                    shell_bytes).decode('utf-8')
-                                request_data = json.dumps(repo_data)
-                                q_param = None
-                                makeapirequestPatch(endpoint + "/" + mem_id, token,q_param,request_data)
-                            else:
+                repo_data = load_file(filename, f)
+
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                data = {'value': ''}
+                if mem_shellScript['value']:
+                    for val in mem_shellScript['value']:
+                        if repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                # If Shell script exists, continue
+                if data['value']:
+                    print("-" * 90)
+                    q_param = None
+                    # Get Shell script details
+                    mem_data = makeapirequest(
+                        ENDPOINT + "/" + data['value']['id'], token)
+                    mem_id = mem_data['id']
+                    # Remove keys before using DeepDiff
+                    mem_data = remove_keys(mem_data)
+
+                    # Check if script data is saved and read the file
+                    if os.path.exists(
+                        configpath +
+                        "/Script Data/" +
+                            repo_data['fileName']):
+                        with open(configpath + "/Script Data/" + repo_data['fileName'], 'r') as f:
+                            repo_payload_config = f.read()
+
+                        mem_payload_config = base64.b64decode(
+                            mem_data['scriptContent']).decode('utf-8')
+
+                        pdiff = DeepDiff(
+                            mem_payload_config,
+                            repo_payload_config,
+                            ignore_order=True).get(
+                            'values_changed',
+                            {})
+                        cdiff = DeepDiff(
+                            mem_data,
+                            repo_data,
+                            ignore_order=True,
+                            exclude_paths="root['scriptContent']").get(
+                            'values_changed',
+                            {})
+
+                        # If any changed values are found, push them to Intune
+                        if pdiff or cdiff:
+                            print(
+                                "Updating Shell script: " +
+                                repo_data['displayName'] +
+                                ", values changed:")
+                            if cdiff:
+                                diff_count += 1
+                                values = get_diff_output(cdiff)
+                                for value in values:
+                                    print(value)
+                            if pdiff:
+                                diff_count += 1
                                 print(
-                                    'No difference found for Shell script: ' + repo_data['displayName'])
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_id,mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['deviceManagementScriptAssignments'] = update
-                                post_assignment_update(request_data,mem_id,'deviceManagement/deviceManagementScripts','assign',token)
-
-                    ## If Shell script does not exist, create it and assign
-                    else:
-                        print("-" * 90)
-                        print("Shell script not found, creating script: " + \
-                              repo_data['displayName'])
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=request_json,status_code=201)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
-                            request_data = {}
-                            request_data['deviceManagementScriptAssignments'] = assignment
-                            post_assignment_update(request_data,post_request['id'],'deviceManagement/deviceManagementScripts','assign',token)
-                        print("Shell script created with id: " + post_request['id'])
\ No newline at end of file
+                                    "Script changed, check commit history for change details")
+                            shell_bytes = repo_payload_config.encode(
+                                'utf-8')
+                            repo_data['scriptContent'] = base64.b64encode(
+                                shell_bytes).decode('utf-8')
+                            request_data = json.dumps(repo_data)
+                            q_param = None
+                            makeapirequestPatch(
+                                ENDPOINT + "/" + mem_id, token, q_param, request_data)
+                        else:
+                            print(
+                                'No difference found for Shell script: ' +
+                                repo_data['displayName'])
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_id, mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {'deviceManagementScriptAssignments': update}
+                            post_assignment_update(
+                                request_data,
+                                mem_id,
+                                'deviceManagement/deviceManagementScripts',
+                                'assign',
+                                token)
+
+                # If Shell script does not exist, create it and assign
+                else:
+                    print("-" * 90)
+                    print("Shell script not found, creating script: " +
+                          repo_data['displayName'])
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {'deviceManagementScriptAssignments': assignment}
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            'deviceManagement/deviceManagementScripts',
+                            'assign',
+                            token)
+                    print(
+                        "Shell script created with id: " +
+                        post_request['id'])
+
+    return diff_count
diff --git a/src/IntuneCD/update_windowsEnrollmentProfile.py b/src/IntuneCD/update_windowsEnrollmentProfile.py
index 42d839ac..af308f4a 100644
--- a/src/IntuneCD/update_windowsEnrollmentProfile.py
+++ b/src/IntuneCD/update_windowsEnrollmentProfile.py
@@ -1,126 +1,143 @@
 #!/usr/bin/env python3
 
 """
-This module updates all Windows Enrollment Profiles in Intune if the configuration in Intune differs from the JSON/YAML file.
-
-Parameters
-----------
-path : str
-    The path to where the backup is saved
-token : str
-    The token to use for authenticating the request
+This module is used to update all Windows Enrollment Profiles in Intune.
 """
 
 import json
 import os
-import yaml
-import re
 
+from deepdiff import DeepDiff
 from .graph_request import makeapirequest, makeapirequestPatch, makeapirequestPost
 from .graph_batch import batch_assignment, get_object_assignment
 from .update_assignment import update_assignment, post_assignment_update
-from deepdiff import DeepDiff
+from .check_file import check_file
+from .load_file import load_file
+from .remove_keys import remove_keys
+from .get_diff_output import get_diff_output
 
-## Set MS Graph endpoint
-endpoint = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles"
+# Set MS Graph endpoint
+ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles"
 
 
 def update(path, token, assignment=False):
-
-    ## Set Windows Enrollment Profile path
-    configpath = path+"/"+"Enrollment Profiles/Windows"
-    ## If Windows Enrollment Profile path exists, continue
-    if os.path.exists(configpath) == True:
-        ## Get enrollment profiles
-        mem_data = makeapirequest(endpoint, token)
-        ## Get current assignment
-        mem_assignments = batch_assignment(mem_data,'deviceManagement/windowsAutopilotDeploymentProfiles/','/assignments',token)
+    """
+    This function updates all Windows Enrollment Profiles 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
+    :param assignment: Boolean to determine if assignments should be updated
+    """
+
+    diff_count = 0
+    # Set Windows Enrollment Profile path
+    configpath = path + "/" + "Enrollment Profiles/Windows"
+    # If Windows Enrollment Profile path exists, continue
+    if os.path.exists(configpath):
+        # Get enrollment profiles
+        mem_data = makeapirequest(ENDPOINT, token)
+        # Get current assignment
+        mem_assignments = batch_assignment(
+            mem_data,
+            'deviceManagement/windowsAutopilotDeploymentProfiles/',
+            '/assignments',
+            token)
 
         for filename in os.listdir(configpath):
-            file = os.path.join(configpath, filename)
-            # If path is Directory, skip
-            if os.path.isdir(file):
+            file = check_file(configpath, filename)
+            if file is False:
                 continue
-            # If file is .DS_Store, skip
-            if filename == ".DS_Store":
-                continue
-            # If file is .md, skip
-            if filename.endswith(".md"):
-                continue
-
-            ## Check which format the file is saved as then open file, load data and set query parameter
+            # Check which format the file is saved as then open file, load data
+            # and set query parameter
             with open(file) as f:
-                    if filename.endswith(".yaml"):
-                        data = json.dumps(yaml.safe_load(f))
-                        repo_data = json.loads(data)
-                        
-                    elif filename.endswith(".json"):
-                        f = open(file)
-                        repo_data = json.load(f)
-
-                    ## Create object to pass in to assignment function
-                    assign_obj = {}
-                    if "assignments" in repo_data:
-                        assign_obj = repo_data['assignments']
-                    repo_data.pop('assignments', None)
-
-                    data = {'value':''}
-                    if mem_data['value']:
-                        for val in mem_data['value']:
-                            if repo_data['displayName'] == val['displayName']:
-                                data['value'] = val
-
-                    ## If Windows Enrollment Profile exists, continue
-                    if data['value']:
-                        print("-" * 90)
-                        mem_id = data['value']['id']
-                        ## Remove keys before using DeepDiff
-                        remove_keys = {'id', 'createdDateTime','version','lastModifiedDateTime'}
-                        for k in remove_keys:
-                            mem_data['value'][0].pop(k, None)
-
-                        diff = DeepDiff(data['value'], repo_data, ignore_order=True).get('values_changed', {})
-
-                        ## If any changed values are found, push them to Intune
-                        if diff:
-                            print("Updating Windows Enrollment profile: " + \
-                                  repo_data['displayName'] + ", values changed:")
-                            for key, value in diff.items():
-                                setting = re.search("\[(.*)\]", key).group(1)
-                                new_val = value['new_value']
-                                old_val = value['old_value']
-                                print(
-                                    f"Setting: {setting}, New Value: {new_val}, Old Value: {old_val}")
-                            if repo_data['managementServiceAppId']:
-                                pass
-                            else:
-                                repo_data['managementServiceAppId'] = ""
-                            request_data = json.dumps(repo_data)
-                            q_param = None
-                            makeapirequestPatch(endpoint + "/" + mem_id, token,q_param,request_data)
+                repo_data = load_file(filename, f)
+
+                # Create object to pass in to assignment function
+                assign_obj = {}
+                if "assignments" in repo_data:
+                    assign_obj = repo_data['assignments']
+                repo_data.pop('assignments', None)
+
+                data = {'value': ''}
+                if mem_data['value']:
+                    for val in mem_data['value']:
+                        if repo_data['displayName'] == val['displayName']:
+                            data['value'] = val
+
+                # If Windows Enrollment Profile exists, continue
+                if data['value']:
+                    print("-" * 90)
+                    mem_id = data['value']['id']
+                    # Remove keys before using DeepDiff
+                    mem_data['value'][0] = remove_keys(mem_data['value'][0])
+
+                    diff = DeepDiff(
+                        data['value'],
+                        repo_data,
+                        ignore_order=True).get(
+                        'values_changed',
+                        {})
+
+                    # If any changed values are found, push them to Intune
+                    if diff:
+                        diff_count += 1
+                        print("Updating Windows Enrollment profile: " +
+                              repo_data['displayName'] + ", values changed:")
+                        values = get_diff_output(diff)
+                        for value in values:
+                            print(value)
+                        if repo_data['managementServiceAppId']:
+                            pass
                         else:
-                            print(
-                                'No difference found for Windows Enrollment profile: ' + repo_data['displayName'])
-
-                        if assignment == True:
-                            mem_assign_obj = get_object_assignment(mem_id,mem_assignments)
-                            update = update_assignment(assign_obj,mem_assign_obj,token)
-                            if update is not None:
-                                request_data = {}
-                                request_data['target'] = update
-                                post_assignment_update(request_data,mem_id,'deviceManagement/windowsAutopilotDeploymentProfiles','assign',token,status_code=201)
-
-                    ## If Autopilot profile does not exist, create it and assign
+                            repo_data['managementServiceAppId'] = ""
+                        request_data = json.dumps(repo_data)
+                        q_param = None
+                        makeapirequestPatch(
+                            ENDPOINT + "/" + mem_id, token, q_param, request_data)
                     else:
-                        print("-" * 90)
                         print(
-                            "Autopilot profile not found, creating profile: " + repo_data['displayName'])
-                        request_json = json.dumps(repo_data)
-                        post_request = makeapirequestPost(endpoint, token,q_param=None,jdata=request_json,status_code=201)
-                        mem_assign_obj = []
-                        assignment = update_assignment(assign_obj,mem_assign_obj,token)
-                        if assignment is not None:
-                            request_data = {}
-                            request_data['target'] = assignment[0]['target']
-                            post_assignment_update(request_data,post_request['id'],'deviceManagement/windowsAutopilotDeploymentProfiles','assignments',token,status_code=201)
-                        print("Autopilot profile created with id: " + post_request['id'])
\ No newline at end of file
+                            'No difference found for Windows Enrollment profile: ' +
+                            repo_data['displayName'])
+
+                    if assignment:
+                        mem_assign_obj = get_object_assignment(
+                            mem_id, mem_assignments)
+                        update = update_assignment(
+                            assign_obj, mem_assign_obj, token)
+                        if update is not None:
+                            request_data = {'target': update}
+                            post_assignment_update(
+                                request_data,
+                                mem_id,
+                                'deviceManagement/windowsAutopilotDeploymentProfiles',
+                                'assign',
+                                token,
+                                status_code=201)
+
+                # If Autopilot profile does not exist, create it and assign
+                else:
+                    print("-" * 90)
+                    print(
+                        "Autopilot profile not found, creating profile: " +
+                        repo_data['displayName'])
+                    request_json = json.dumps(repo_data)
+                    post_request = makeapirequestPost(
+                        ENDPOINT, token, q_param=None, jdata=request_json, status_code=201)
+                    mem_assign_obj = []
+                    assignment = update_assignment(
+                        assign_obj, mem_assign_obj, token)
+                    if assignment is not None:
+                        request_data = {'target': assignment[0]['target']}
+                        post_assignment_update(
+                            request_data,
+                            post_request['id'],
+                            'deviceManagement/windowsAutopilotDeploymentProfiles',
+                            'assignments',
+                            token,
+                            status_code=201)
+                    print(
+                        "Autopilot profile created with id: " +
+                        post_request['id'])
+
+    return diff_count


Test patch:
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
new file mode 100644
index 00000000..6b48fc4e
--- /dev/null
+++ b/.github/workflows/unit-test.yml
@@ -0,0 +1,47 @@
+# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
+# For more information see https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+
+name: Unit tests
+
+on:
+  push:
+    branches: [ "main" ]
+  pull_request:
+    branches: [ "main" ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ["3.8", "3.9", "3.10"]
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v3
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        python -m pip install flake8 pytest
+        if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi
+    - name: Lint with flake8
+      run: |
+        # stop the build if there are Python syntax errors or undefined names
+        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
+        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
+        flake8 . --count --exit-zero --max-complexity=35 --max-line-length=127 --statistics
+    - name: Test with pytest
+      run: |
+        pytest --cov-report=xml --cov=src/IntuneCD tests/
+    - name: Upload coverage to Codecov
+      uses: codecov/codecov-action@v3
+      if: matrix.python-version == 3.8
+      with:
+        files: ./coverage.xml
+        flags: unittests
+        fail_ci_if_error: true
\ No newline at end of file
diff --git a/test-requirements.txt b/test-requirements.txt
new file mode 100644
index 00000000..ae83ba54
--- /dev/null
+++ b/test-requirements.txt
@@ -0,0 +1,8 @@
+pyyaml>=6.0
+testfixtures>=7.0
+deepdiff>=5.6.0
+requests
+adal
+pytest==6.2.5
+pytest-cov==3.0.0
+pytablewriter
\ No newline at end of file
diff --git a/tests/Backup/__init__.py b/tests/Backup/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/Backup/test_backup_apns.py b/tests/Backup/test_backup_apns.py
new file mode 100644
index 00000000..191b774d
--- /dev/null
+++ b/tests/Backup/test_backup_apns.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up APNS."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_apns import savebackup
+
+APNS = {
+    "@odata.context": "https://graph.microsoft.com/beta/$metadata#deviceManagement/applePushNotificationCertificate/$entity",
+    "id": "00000000-0000-0000-0000-000000000000",
+    "appleIdentifier": "awesome@example.com",
+    "topicIdentifier": "com.apple.mgmt.External.ef285859-4227-415c-8b08-826b610f2034",
+    "lastModifiedDateTime": "2022-04-01T14:10:18Z",
+    "expirationDateTime": "2023-04-01T13:59:54Z",
+    "certificateUploadStatus": None,
+    "certificateUploadFailureReason": None,
+    "certificateSerialNumber": "11000000000000",
+    "certificate": None}
+
+
+@patch("src.IntuneCD.backup_apns.savebackup")
+@patch("src.IntuneCD.backup_apns.makeapirequest", return_value=APNS)
+class TestBackupAPNS(unittest.TestCase):
+    """Test class for backup_apns."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Apple Push Notification/awesome@example.com."
+        self.expected_data = {
+            "appleIdentifier": "awesome@example.com",
+            "expirationDateTime": "2023-04-01T13:59:54Z",
+            "certificateUploadStatus": None,
+            "certificateUploadFailureReason": None,
+            "certificateSerialNumber": "11000000000000"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Apple Push Notification').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Apple Push Notification').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_return_data(self, mock_data, mock_makeapirequest):
+        """The count should be 0 if no data is returned."""
+
+        mock_data.return_value = None
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_appConfiguration.py b/tests/Backup/test_backup_appConfiguration.py
new file mode 100644
index 00000000..6ad55129
--- /dev/null
+++ b/tests/Backup/test_backup_appConfiguration.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up App Configuration."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_appConfiguration import savebackup
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupAppConfig(unittest.TestCase):
+    """Test class for backup_appConfiguration."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/App Configuration/test_iosMobileAppConfiguration."
+        self.expected_data = {
+            '@odata.type': '#microsoft.graph.iosMobileAppConfiguration',
+            'assignments': [
+                {
+                    'target': {
+                        'groupName': 'Group1'}}],
+            'displayName': 'test',
+            'settings': [
+                    {
+                        'appConfigKey': 'sharedDevice',
+                        'appConfigKeyType': 'booleanType',
+                        'appConfigKeyValue': 'true'}],
+            'targetedMobileApps': {
+                'appName': 'Microsoft Authenticator',
+                'type': '#microsoft.graph.iosVppApp'}}
+        self.app_config = {
+            '@odata.context': 'https://graph.microsoft.com/beta/$metadata#deviceAppManagement/mobileAppConfigurations',
+            'value': [
+                {
+                    '@odata.type': '#microsoft.graph.iosMobileAppConfiguration',
+                    'id': '0',
+                    'targetedMobileApps': ['0'],
+                    'displayName': 'test',
+                    'settings': [
+                        {
+                            'appConfigKey': 'sharedDevice',
+                            'appConfigKeyType': 'booleanType',
+                            'appConfigKeyValue': 'true'}]}]}
+        self.app_data = {
+            '@odata.type': '#microsoft.graph.iosVppApp',
+            'id': '0',
+            'displayName': 'Microsoft Authenticator',
+            'largeIcon': {
+                'value': '/'},
+            'licensingType': {
+                'supportsDeviceLicensing': True},
+            'applicableDeviceType': {
+                'iPhoneAndIPod': True},
+            'revokeLicenseActionResults': []}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_appConfiguration.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_appConfiguration.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_appConfiguration.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.side_effect = self.app_config, self.app_data
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'yaml',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/App Configuration').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/App Configuration').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+        self.makeapirequest.side_effect = [{'value': []}]
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_appProtection.py b/tests/Backup/test_backup_appProtection.py
new file mode 100644
index 00000000..a22d2d42
--- /dev/null
+++ b/tests/Backup/test_backup_appProtection.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up App Protection."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_AppProtection import savebackup
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupAppProtection(unittest.TestCase):
+    """Test class for backup_appProtection."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/App Protection/test_iosManagedAppProtection."
+        self.saved_targetedAppManagementLevels = f"{self.directory.path}/App Protection/test_test."
+        self.expected_data = {"@odata.type": "#microsoft.graph.iosManagedAppProtection",
+                              "displayName": "test",
+                              "description": "",
+                              "roleScopeTagIds": ["0"],
+                              "exemptedAppProtocols": [{"name": "Default",
+                                                        "value": "skype;app-settings;calshow;itms;itmss;itms-apps;itms-appss;itms-services;"}],
+                              "assignments": [{"target": {"groupName": "Group1"}}]}
+        self.app_protection = {
+            '@odata.context': 'https://graph.microsoft.com/beta/$metadata#deviceAppManagement/managedAppPolicies',
+            'value': [
+                {
+                    '@odata.type': '#microsoft.graph.iosManagedAppProtection',
+                    'displayName': 'test',
+                    'id': 'T_0',
+                    'description': '',
+                    'roleScopeTagIds': ['0'],
+                    'version': '"0"',
+                    'exemptedAppProtocols': [
+                        {
+                            'name': 'Default',
+                            'value': 'skype;app-settings;calshow;itms;itmss;itms-apps;itms-appss;itms-services;'}]}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_AppProtection.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_AppProtection.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_AppProtection.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.app_protection
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'yaml',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(Path(f'{self.directory.path}/App Protection').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(Path(f'{self.directory.path}/App Protection').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_targetedManagedAppConfiguration(self):
+        """The count should be 0 since the targetedManagedAppConfiguration is not supported."""
+
+        self.makeapirequest.return_value = {'value': [
+            {'@odata.type': '#microsoft.graph.targetedManagedAppConfiguration'}]}
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+        self.assertEqual(0, self.count)
+
+    def test_backup_targetedAppManagementLevels(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.app_protection['value'][0]['targetedAppManagementLevels'] = "test"
+        self.expected_data['targetedAppManagementLevels'] = "test"
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_targetedAppManagementLevels + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(Path(f'{self.directory.path}/App Protection').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        self.makeapirequest.return_value = {'value': []}
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_appleEnrollmentProfile.py b/tests/Backup/test_backup_appleEnrollmentProfile.py
new file mode 100644
index 00000000..4eaf60da
--- /dev/null
+++ b/tests/Backup/test_backup_appleEnrollmentProfile.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Apple Enrollment Profile."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from src.IntuneCD.backup_appleEnrollmentProfile import savebackup
+from testfixtures import TempDirectory
+
+TOKEN_RESPONSE = {
+    '@odata.context': 'https://graph.microsoft.com/beta/$metadata#deviceManagement/depOnboardingSettings',
+    '@odata.count': 0,
+    'value': [
+        {
+            'id': '0000000',
+            'appleIdentifier': 'awesome@example.com',
+            'tokenType': 'dep',
+            'tokenName': 'Test',
+            'roleScopeTagIds': ['0']}]}
+BATCH_INTUNE = [
+    {
+        'value': [
+            {
+                '@odata.type': '#microsoft.graph.depMacOSEnrollmentProfile',
+                'id': '0',
+                'displayName': 'test',
+                'description': '',
+                'requiresUserAuthentication': True,
+                'configurationEndpointUrl': 'https://appleconfigurator2.manage.microsoft.com/MDMServiceConfig?id=0',
+                'enableAuthenticationViaCompanyPortal': False,
+                'requireCompanyPortalOnSetupAssistantEnrolledDevices': False,
+                'isDefault': True}]}]
+
+
+@patch("src.IntuneCD.backup_appleEnrollmentProfile.savebackup")
+@patch("src.IntuneCD.backup_appleEnrollmentProfile.makeapirequest",
+       return_value=TOKEN_RESPONSE)
+@patch("src.IntuneCD.backup_appleEnrollmentProfile.batch_request",
+       return_value=BATCH_INTUNE)
+class TestBackupAppleEnrollmentProfile(unittest.TestCase):
+    """Test class for backup_appleEnrollmentProfile."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Enrollment Profiles/Apple/test."
+        self.expected_data = {
+            '@odata.type': '#microsoft.graph.depMacOSEnrollmentProfile',
+            'displayName': 'test',
+            'description': '',
+            'requiresUserAuthentication': True,
+            'configurationEndpointUrl': 'https://appleconfigurator2.manage.microsoft.com/MDMServiceConfig?id=0',
+            'enableAuthenticationViaCompanyPortal': False,
+            'requireCompanyPortalOnSetupAssistantEnrolledDevices': False,
+            'isDefault': True}
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(
+            self,
+            mock_data,
+            mock_makeapirequest,
+            mock_batch_request):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(Path(f'{self.directory.path}/Enrollment Profiles/Apple').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(
+            self,
+            mock_data,
+            mock_makeapirequest,
+            mock_batch_request):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(Path(f'{self.directory.path}/Enrollment Profiles/Apple').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_return_data(
+            self,
+            mock_data,
+            mock_makeapirequest,
+            mock_batch_request):
+        """The count should be 0 if no data is returned."""
+
+        mock_makeapirequest.return_value = {'value': []}
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_applications.py b/tests/Backup/test_backup_applications.py
new file mode 100644
index 00000000..b256beea
--- /dev/null
+++ b/tests/Backup/test_backup_applications.py
@@ -0,0 +1,294 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up applications."""
+
+
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from src.IntuneCD.backup_applications import savebackup
+from testfixtures import TempDirectory
+
+
+class TestBackupApplications(unittest.TestCase):
+    """Test class for backup_applications."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.app_base_data = {'value': [{
+            "@odata.type": '',
+            "id": "0",
+            "displayName": "test",
+            "vppTokenAppleId": "test@test.com"}]}
+        self.batch_assignment_data = [
+            {
+                'value': [
+                    {
+                        'target': {
+                            'groupId': '00000-0000-0000-0000-000000000000',
+                            'groupName': 'Group1'}}]}]
+        self.object_assignment_data = [
+            {
+                'target': {
+                    'groupName': 'Group1'}}]
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_applications.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = self.batch_assignment_data
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_applications.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = self.object_assignment_data
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_applications.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_ios_vpp_app(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.iosVppApp'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/iOS').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/iOS/test_iOSVppApp_test.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_macOS_vpp_app(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.macOsVppApp'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/macOS').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/macOS/test_macOSVppApp_test.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_win32_lob_app_and_displayVersion(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.win32LobApp'
+        self.app_base_data['value'][0]['displayVersion'] = '1.0.0'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Windows').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Windows/test_Win32_1_0_0.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_win32_lob_app_no_displayVersion(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.win32LobApp'
+        self.app_base_data['value'][0]['displayVersion'] = None
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Windows').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Windows/test_Win32.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_msi_app(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.windowsMobileMSI'
+        self.app_base_data['value'][0]['productVersion'] = '1.0.0'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Windows').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Windows/test_WinMSI_1_0_0.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_android_app(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.androidManagedStoreApp'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Android').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Android/test_androidManagedStoreApp.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_microsoft_app(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.microsoftApp'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Windows').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Windows/test_microsoftApp.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_office_suite_app(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.microsoftOfficeSuiteApp'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Office Suite').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Office Suite/test_microsoftOfficeSuiteApp.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_web_app(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.webApp'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Web App').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/Web App/test_webApp.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_other_app(self):
+        """The folder should be created, the file should be created, and the count should be 1."""
+
+        self.app_base_data['value'][0]['@odata.type'] = '#microsoft.graph.macOSother'
+        self.makeapirequest.return_value = self.app_base_data
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/macOS').exists())
+        self.assertTrue(
+            Path(
+                self.directory.path +
+                '/Applications/macOS/test_macOSother.json').exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+        self.makeapirequest.return_value = {"value": []}
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_assignmentFilter.py b/tests/Backup/test_backup_assignmentFilter.py
new file mode 100644
index 00000000..f76ae551
--- /dev/null
+++ b/tests/Backup/test_backup_assignmentFilter.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up assignment filters."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_assignmentFilters import savebackup
+
+ASSIGNMENT_FILTER = {
+    "value": [
+        {
+            "displayName": "macOS - Model",
+            "description": "",
+            "id": "0",
+            "platform": "macOS",
+            "rule": "(device.model -eq \"macbook Pro\")",
+            "roleScopeTags": ["0"]
+        }]}
+
+
+@patch("src.IntuneCD.backup_assignmentFilters.savebackup")
+@patch("src.IntuneCD.backup_assignmentFilters.makeapirequest",
+       return_value=ASSIGNMENT_FILTER)
+class TestBackupAssignmentFilters(unittest.TestCase):
+    """Test class for backup_assignmentFilters."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Filters/macOS - Model."
+        self.expected_data = {
+            "displayName": "macOS - Model",
+            "description": "",
+            "platform": "macOS",
+            "rule": "(device.model -eq \"macbook Pro\")",
+            "roleScopeTags": [
+                "0"
+            ]}
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(Path(f'{self.directory.path}/Filters').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(Path(f'{self.directory.path}/Filters').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_return_data(self, mock_data, mock_makeapirequest):
+        """The count should be 0 if no data is returned."""
+
+        mock_data.return_value = {'value': []}
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_compliance.py b/tests/Backup/test_backup_compliance.py
new file mode 100644
index 00000000..48e360ff
--- /dev/null
+++ b/tests/Backup/test_backup_compliance.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up compliance."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_compliance import savebackup
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupCompliance(unittest.TestCase):
+    """Test class for backup_compliance."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/Compliance Policies/Policies/test_iosCompliancePolicy."
+        self.expected_data = {"@odata.type": "#microsoft.graph.iosCompliancePolicy",
+                              "roleScopeTagIds": ["0"],
+                              "description": "Description value",
+                              "displayName": "test",
+                              "scheduledActionsForRule": [{"ruleName": None,
+                                                           "scheduledActionConfigurations": [{"gracePeriodHours": 0}]}],
+                              "assignments": [{"target": {"groupName": "Group1"}}]}
+        self.compliance_policy = {
+            "value": [{
+                "@odata.type": "#microsoft.graph.iosCompliancePolicy",
+                "roleScopeTagIds": ["0"],
+                "id": "0",
+                "description": "Description value",
+                "displayName": "test",
+                "scheduledActionsForRule": [
+                    {
+                        "ruleName": None,
+                        "scheduledActionConfigurations": [
+                            {
+                                "id": "0",
+                                "gracePeriodHours": 0}]}]}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_compliance.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_compliance.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_compliance.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.compliance_policy
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        output = 'yaml'
+        count = savebackup(
+            self.directory.path,
+            output,
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + output, 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            saved_data = json.loads(data)
+
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+        self.assertTrue(Path(f'{self.directory.path}/Compliance Policies/Policies').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        output = 'json'
+        count = savebackup(
+            self.directory.path,
+            output,
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + output, 'r') as f:
+            saved_data = json.load(f)
+
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+        self.assertTrue(Path(f'{self.directory.path}/Compliance Policies/Policies').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        self.makeapirequest.return_value = {'value': []}
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_compliancePartner.py b/tests/Backup/test_backup_compliancePartner.py
new file mode 100644
index 00000000..53564af9
--- /dev/null
+++ b/tests/Backup/test_backup_compliancePartner.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up compliance partner."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_compliancePartner import savebackup
+
+COMPLIANCE_PARTNER = {
+    "value": [{
+        "@odata.type": "#microsoft.graph.complianceManagementPartner",
+        "id": "0",
+        "partnerState": "unavailable",
+        "displayName": "test",
+        "macOsOnboarded": True,
+        "macOsEnrollmentAssignments": [
+            {
+                "@odata.type": "microsoft.graph.complianceManagementPartnerAssignment",
+                "target": {
+                    "deviceAndAppManagementAssignmentFilterId": "0"
+                }
+            }
+        ]
+    }]}
+
+
+@patch("src.IntuneCD.backup_compliancePartner.savebackup")
+@patch("src.IntuneCD.backup_compliancePartner.makeapirequest",
+       return_value=COMPLIANCE_PARTNER)
+class TestBackupCompliancePartner(unittest.TestCase):
+    """Test class for backup_assignmentFilters."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Partner Connections/Compliance/test."
+        self.expected_data = {
+            "@odata.type": "#microsoft.graph.complianceManagementPartner",
+            "partnerState": "unavailable",
+            "displayName": "test",
+            "macOsOnboarded": True,
+            "macOsEnrollmentAssignments": [
+                {
+                    "@odata.type": "microsoft.graph.complianceManagementPartnerAssignment",
+                    "target": {
+                        "deviceAndAppManagementAssignmentFilterId": "0"}}]}
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Partner Connections/Compliance').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Partner Connections/Compliance').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_return_data(self, mock_data, mock_makeapirequest):
+        """The count should be 0 if no data is returned."""
+
+        mock_data.return_value = {"value": [{"partnerState": "unknown"}]}
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_configurationPolicies.py b/tests/Backup/test_backup_configurationPolicies.py
new file mode 100644
index 00000000..953f1ea3
--- /dev/null
+++ b/tests/Backup/test_backup_configurationPolicies.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up App Configuration."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_configurationPolicies import savebackup
+
+BATCH_REQUEST = [
+    {
+        '@odata.context': "https://graph.microsoft.com/beta/$metadata#deviceManagement/configurationPolicies('0')/settings",
+        '@odata.count': 2,
+        'value': [
+            {
+                'id': '0',
+                'settingInstance': {
+                    '@odata.type': '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance',
+                    'settingDefinitionId': 'com.apple.managedclient.preferences_enforcementlevel',
+                    'settingInstanceTemplateReference': None,
+                    'choiceSettingValue': {
+                        'settingValueTemplateReference': None,
+                        'value': 'com.apple.managedclient.preferences_enforcementlevel_0',
+                        'children': []}}}]}]
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupConfigurationPolicy(unittest.TestCase):
+    """Test class for backup_appConfiguration."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/Settings Catalog/test."
+        self.expected_data = {
+            '@odata.type': '#microsoft.graph.deviceManagementConfigurationPolicy',
+            'assignments': [
+                {
+                    'target': {
+                        'groupName': 'Group1'}}],
+            'description': 'Description value',
+            'name': 'test',
+            'roleScopeTagIds': ['Role Scope Tag Ids value'],
+            'settings': [
+                    {
+                        'id': '0',
+                        'settingInstance': {
+                            '@odata.type': '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance',
+                            'choiceSettingValue': {
+                                'children': [],
+                                'settingValueTemplateReference': None,
+                                'value': 'com.apple.managedclient.preferences_enforcementlevel_0'},
+                            'settingDefinitionId': 'com.apple.managedclient.preferences_enforcementlevel',
+                            'settingInstanceTemplateReference': None}}],
+            'templateReference': {
+                '@odata.type': 'microsoft.graph.deviceManagementConfigurationPolicyTemplateReference',
+                'templateId': 'Template Id value'}}
+        self.configuration_policy = {
+            "value": [{
+                "@odata.type": "#microsoft.graph.deviceManagementConfigurationPolicy",
+                "id": "0",
+                "name": "test",
+                "description": "Description value",
+                "roleScopeTagIds": [
+                    "Role Scope Tag Ids value"
+                ],
+                "isAssigned": True,
+                "templateReference": {
+                    "@odata.type": "microsoft.graph.deviceManagementConfigurationPolicyTemplateReference",
+                    "templateId": "Template Id value"
+                }
+            }
+            ]
+        }
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_configurationPolicies.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.batch_request_patch = patch(
+            'src.IntuneCD.backup_configurationPolicies.batch_request')
+        self.batch_request = self.batch_request_patch.start()
+        self.batch_request.return_value = BATCH_REQUEST
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_configurationPolicies.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_configurationPolicies.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.configuration_policy
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.batch_request.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'yaml',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(Path(f'{self.directory.path}/Settings Catalog').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(Path(f'{self.directory.path}/Settings Catalog').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        self.makeapirequest.return_value = {'value': []}
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_groupPolicyConfiguration.py b/tests/Backup/test_backup_groupPolicyConfiguration.py
new file mode 100644
index 00000000..06b6eb2a
--- /dev/null
+++ b/tests/Backup/test_backup_groupPolicyConfiguration.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Group Policy Configurations."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from src.IntuneCD.backup_groupPolicyConfiguration import savebackup
+from testfixtures import TempDirectory
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupGroupPolicyConfiguration(unittest.TestCase):
+    """Test class for backup_groupPolicyConfiguration."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/Group Policy Configurations/test."
+        self.expected_data = {
+            'assignments': [
+                {
+                    'target': {
+                        'groupName': 'Group1'}}],
+            'definitionValues': [
+                {
+                    'classType': 'machine',
+                    'displayName': 'Default search provider icon',
+                    'id': '0',
+                    'policyType': 'admxIngested',
+                    'presentationValues': [],
+                    'version': '1.0'}],
+            'description': '',
+            'displayName': 'test',
+            'roleScopeTagIds': ['0']}
+        self.group_policy = {"value": [{
+            "displayName": "test",
+            "description": "",
+            "id": "0",
+            "roleScopeTagIds": ["0"]
+        }]}
+        self.definitions = {"value": [{
+            "classType": "machine",
+            "displayName": "Default search provider icon",
+            "policyType": "admxIngested",
+            "version": "1.0",
+            "id": "0"
+        }]}
+        self.presentations = {"value": []}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_groupPolicyConfiguration.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_groupPolicyConfiguration.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_groupPolicyConfiguration.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.side_effect = self.group_policy, self.definitions, self.presentations
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'yaml',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            saved_data = json.loads(data)
+
+        self.assertTrue(Path(f'{self.directory.path}/Group Policy Configurations').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            saved_data = json.load(f)
+
+        self.assertTrue(Path(f'{self.directory.path}/Group Policy Configurations').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        self.makeapirequest.side_effect = [{'value': []}]
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_managedGPlay.py b/tests/Backup/test_backup_managedGPlay.py
new file mode 100644
index 00000000..c55f9b59
--- /dev/null
+++ b/tests/Backup/test_backup_managedGPlay.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Managed Google Play."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_managedGPlay import savebackup
+
+MANAGED_GPLAY = {
+    "id": "0",
+    "bindStatus": "boundAndValidated",
+          "lastAppSyncDateTime": "2022-01-28T12:28:48.975089Z",
+          "lastAppSyncStatus": "success",
+          "ownerUserPrincipalName": "awesome@gmail.com",
+}
+
+
+@patch("src.IntuneCD.backup_managedGPlay.savebackup")
+@patch("src.IntuneCD.backup_managedGPlay.makeapirequest",
+       return_value=MANAGED_GPLAY)
+class TestBackupManagedGPlay(unittest.TestCase):
+    """Test class for backup_managedGPlay."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Managed Google Play/awesome@gmail.com."
+        self.expected_data = {
+            "bindStatus": "boundAndValidated",
+            "lastAppSyncDateTime": "2022-01-28T12:28:48.975089Z",
+            "lastAppSyncStatus": "success",
+            "ownerUserPrincipalName": "awesome@gmail.com",
+        }
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Managed Google Play').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Managed Google Play').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_return_data(self, mock_data, mock_makeapirequest):
+        """The count should be 0 if no data is returned."""
+
+        mock_data.return_value = None
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_managementIntents.py b/tests/Backup/test_backup_managementIntents.py
new file mode 100644
index 00000000..a5a70dd9
--- /dev/null
+++ b/tests/Backup/test_backup_managementIntents.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Management Intents."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from src.IntuneCD.backup_managementIntents import savebackup
+from testfixtures import TempDirectory
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupManagementIntent(unittest.TestCase):
+    """Test class for backup_managementIntent."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/Management Intents/Dummy Intent/test."
+        self.expected_data = {
+            'assignments': [{'target': {'groupName': 'Group1'}}],
+            'description': 'Hi there',
+            "displayName": "test",
+            "templateId": "0",
+            "roleScopeTagIds": ["0"],
+            "settingsDelta": [{"value": True}]
+        }
+        self.intent = {"value": [{
+            "displayName": "test",
+            "id": "0",
+        }]}
+        self.template = {"value": [{
+            "displayName": "Dummy Intent",
+            "id": "0",
+        }]}
+        self.batch_intent_data = {"value": [{
+            "id": "0",
+            "displayName": "test",
+            "description": "Hi there",
+            "templateId": "0",
+            "settingsDelta": [{"value": True}],
+            "roleScopeTagIds": ["0"]
+        }]}
+
+        self.batch_intent_patch = patch(
+            'src.IntuneCD.backup_managementIntents.batch_intents')
+        self.batch_intent = self.batch_intent_patch.start()
+        self.batch_intent.return_value = self.batch_intent_data
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_managementIntents.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_managementIntents.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_managementIntents.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.side_effect = self.intent, self.template
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.batch_intent.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'yaml',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Management Intents/Dummy Intent').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Management Intents/Dummy Intent').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        self.batch_intent.return_value = {"value": []}
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_managementPartner.py b/tests/Backup/test_backup_managementPartner.py
new file mode 100644
index 00000000..d4f6a72b
--- /dev/null
+++ b/tests/Backup/test_backup_managementPartner.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up management partner."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_managementPartner import savebackup
+
+MANAGEMENT_PARTNER = {
+    "value": [{
+        "@odata.type": "#microsoft.graph.managementPartner",
+        "id": "0",
+        "isConfigured": True,
+        "displayName": "test",
+    }]}
+
+
+@patch("src.IntuneCD.backup_managementPartner.savebackup")
+@patch("src.IntuneCD.backup_managementPartner.makeapirequest",
+       return_value=MANAGEMENT_PARTNER)
+class TestBackupManagementPartner(unittest.TestCase):
+    """Test class for backup_managementPartner."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Partner Connections/Management/test."
+        self.expected_data = {
+            "@odata.type": "#microsoft.graph.managementPartner",
+            "isConfigured": True,
+            "displayName": "test", }
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Partner Connections/Management').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Partner Connections/Management').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_return_data(self, mock_data, mock_makeapirequest):
+        """The count should be 0 if no data is returned."""
+
+        mock_data.return_value = {"value": [{"isConfigured": False}]}
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_notificationTemplate.py b/tests/Backup/test_backup_notificationTemplate.py
new file mode 100644
index 00000000..90ddbac4
--- /dev/null
+++ b/tests/Backup/test_backup_notificationTemplate.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Notification Templates."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_notificationTemplate import savebackup
+
+
+def side_effects_makeapirequest():
+    """Mock function for makeapirequest."""
+
+    MESSAGE_TEMPLATE = {
+        'value': [{'id': '0',
+                   'displayName': 'test',
+                   'defaultLocale': 'da-dk',
+                   'brandingOptions': 'includeCompanyLogo,includeCompanyName,includeContactInformation',
+                   'roleScopeTagIds': ['0'],
+                   'localizedNotificationMessages': [{'id': '0',
+                                                      'lastModifiedDateTime': '2022-07-16T00:01:14.8680508Z',
+                                                      'locale': 'da-dk',
+                                                      'subject': 'test',
+                                                      'messageTemplate': 'Danish locale demo new',
+                                                      'isDefault': True}]}]}
+
+    LOCALIZED_MESSAGES = {
+        'id': '0',
+        'displayName': 'test',
+        'defaultLocale': 'da-dk',
+        'brandingOptions': 'includeCompanyLogo,includeCompanyName,includeContactInformation',
+        'roleScopeTagIds': ['0'],
+        'localizedNotificationMessages': [
+            {
+                'id': '0',
+                'lastModifiedDateTime': '2022-07-16T00:03:39.8347438Z',
+                'locale': 'da-dk',
+                'subject': 'test',
+                'messageTemplate': 'Danish locale demo new',
+                'isDefault': True}]}
+
+    return MESSAGE_TEMPLATE, LOCALIZED_MESSAGES
+
+
+@patch("src.IntuneCD.backup_notificationTemplate.savebackup")
+@patch("src.IntuneCD.backup_notificationTemplate.makeapirequest",
+       side_effect=side_effects_makeapirequest())
+class TestBackupNotificationTemplate(unittest.TestCase):
+    """Test class for backup_notificationTemplate."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Compliance Policies/Message Templates/test."
+        self.expected_data = {
+            'brandingOptions': 'includeCompanyLogo,includeCompanyName,includeContactInformation',
+            'defaultLocale': 'da-dk',
+            'displayName': 'test',
+            'localizedNotificationMessages': [
+                {
+                    'isDefault': True,
+                    'locale': 'da-dk',
+                    'messageTemplate': 'Danish locale demo new',
+                    'subject': 'test'}],
+            'roleScopeTagIds': ['0']}
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Compliance Policies/Message Templates').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Compliance Policies/Message Templates').exists())
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_return_data(self, mock_data, mock_makeapirequest):
+        """The count should be 0 if no data is returned."""
+
+        mock_data.side_effect = [{"value": []}]
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_powershellScripts.py b/tests/Backup/test_backup_powershellScripts.py
new file mode 100644
index 00000000..1fd0971d
--- /dev/null
+++ b/tests/Backup/test_backup_powershellScripts.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Powershell Scripts."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from src.IntuneCD.backup_powershellScripts import savebackup
+from testfixtures import TempDirectory
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupPowershellScript(unittest.TestCase):
+    """Test class for backup_powershellScript."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/Scripts/Powershell/test."
+        self.script_content_path = f"{self.directory.path}/Scripts/Powershell/Script Data/test.ps1"
+        self.expected_data = {
+            'assignments': [
+                {
+                    'target': {
+                        'groupName': 'Group1'}}],
+            'displayName': 'test',
+            'fileName': 'test',
+            'scriptContent': 'WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE='}
+        self.script_policy_data = {
+            "value": [{"id": "0", "displayName": "test"}]}
+        self.batch_request_data = [
+            {
+                'id': '0',
+                'displayName': 'test',
+                'fileName': 'test',
+                'scriptContent': 'WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE='}]
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_powershellScripts.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_powershellScripts.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.batch_request_patch = patch(
+            'src.IntuneCD.backup_powershellScripts.batch_request')
+        self.batch_request = self.batch_request_patch.start()
+        self.batch_request.return_value = self.batch_request_data
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_powershellScripts.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.script_policy_data
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.batch_request.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'yaml',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+        self.assertTrue(
+            Path(f'{self.directory.path}/Scripts/Powershell').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Scripts/Powershell').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_script_is_created(self):
+        """The script data folder should be created and a .ps1 file should be created."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Scripts/Powershell/Script Data').exists())
+        self.assertTrue(self.script_content_path)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        self.makeapirequest.return_value = {"value": []}
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_proactiveRemediation.py b/tests/Backup/test_backup_proactiveRemediation.py
new file mode 100644
index 00000000..26f137f8
--- /dev/null
+++ b/tests/Backup/test_backup_proactiveRemediation.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Proactive Remediation."""
+
+import json
+import yaml
+import unittest
+
+from unittest.mock import patch
+from src.IntuneCD.backup_proactiveRemediation import savebackup
+from testfixtures import TempDirectory
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupProactiveRemediation(unittest.TestCase):
+    """Test class for backup_proactiveRemediation."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/Proactive Remediations/test."
+        self.detection_script_path = f"{self.directory.path}/Proactive Remediations/Script Data/test__DetectionScript.ps1"
+        self.remediation_script_path = f"{self.directory.path}/Proactive Remediations/Script Data/test__RemediationScript.ps1"
+        self.expected_data = {
+            'assignments': [
+                {
+                    'target': {
+                        'groupName': 'Group1'}}],
+            'displayName': 'test',
+            'publisher': 'test',
+            'detectionScriptContent': 'WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE=',
+            'remediationScriptContent': 'WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE='}
+        self.script_policy_data = {
+            "value": [{"id": "0", "displayName": "test"}]}
+        self.batch_request_data = [
+            {
+                'id': '0',
+                'displayName': 'test',
+                "publisher": "test",
+                'detectionScriptContent': 'WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE=',
+                'remediationScriptContent': 'WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE='}]
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_proactiveRemediation.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_proactiveRemediation.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.batch_request_patch = patch(
+            'src.IntuneCD.backup_proactiveRemediation.batch_request')
+        self.batch_request = self.batch_request_patch.start()
+        self.batch_request.return_value = self.batch_request_data
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_proactiveRemediation.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.script_policy_data
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.batch_request.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'yaml',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(f'{self.directory.path}/Proactive Remediations')
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(3, self.count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(f'{self.directory.path}/Proactive Remediations')
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(3, self.count)
+
+    def test_detection_script_is_created(self):
+        """The folder should be created and a .ps1 file should be created."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            f'{self.directory.path}/Proactive Remediations/Script Data')
+        self.assertTrue(self.detection_script_path)
+
+    def test_remediation_script_is_created(self):
+        """The folder should be created and a .ps1 file should be created."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            f'{self.directory.path}/Proactive Remediations/Script Data')
+        self.assertTrue(self.remediation_script_path)
+
+    def test_publisher_is_microsoft(self):
+        """Microsoft should not be backed up."""
+
+        with patch('src.IntuneCD.backup_proactiveRemediation.batch_request',
+                   side_effect=[[{'publisher': 'Microsoft'}]]):
+            self.count = savebackup(
+                self.directory.path,
+                'json',
+                self.exclude,
+                self.token)
+
+        self.assertEqual(0, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        with patch('src.IntuneCD.backup_proactiveRemediation.makeapirequest',
+                   return_value={'value': []}):
+            self.count = savebackup(
+                self.directory.path,
+                'json',
+                self.exclude,
+                self.token)
+        """No data should be backed up."""
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_profiles.py b/tests/Backup/test_backup_profiles.py
new file mode 100644
index 00000000..a79a2b83
--- /dev/null
+++ b/tests/Backup/test_backup_profiles.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up profiles."""
+
+
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_profiles import savebackup
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupProfiles(unittest.TestCase):
+    """Test class for backup_profiles."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_profiles.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_profiles.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_profiles.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_macOS_custom_profile(self):
+        """The folders and files should be created and the count should be 2."""
+
+        self.makeapirequest.return_value = {'value': [{
+            "@odata.type": '#microsoft.graph.macOSCustomConfiguration',
+            "id": "0",
+            "displayName": "test",
+            "payload": "SGkgdGhlcmUgcHJldHR5",
+                       "payloadFileName": "test.mobileconfig"}]}
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.token,
+            self.exclude)
+
+        self.assertTrue(Path(
+            f"{self.directory.path}/Device Configurations/test_macOSCustomConfiguration.json").exists())
+        self.assertTrue(Path(
+            f"{self.directory.path}/Device Configurations/mobileconfig/test.mobileconfig").exists())
+        self.assertEqual(2, self.count)
+
+    def test_backup_ios_custom_profile(self):
+        """The folders and files should be created and the count should be 2."""
+
+        self.makeapirequest.return_value = {'value': [{
+            "@odata.type": '#microsoft.graph.iosCustomConfiguration',
+            "id": "0",
+            "displayName": "test",
+            "payload": "SGkgdGhlcmUgcHJldHR5",
+                       "payloadFileName": "test.mobileconfig"}]}
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.token,
+            self.exclude)
+
+        self.assertTrue(Path(
+            f"{self.directory.path}/Device Configurations/test_iosCustomConfiguration.json").exists())
+        self.assertTrue(Path(
+            f"{self.directory.path}/Device Configurations/mobileconfig/test.mobileconfig").exists())
+        self.assertEqual(2, self.count)
+
+    def test_backup_windows_custom_profile_encrypted(self):
+        """The file should be created and the count should be 1."""
+
+        self.profile = {'value': [{
+            "@odata.type": "#microsoft.graph.windows10CustomConfiguration",
+            "id": "0",
+            "displayName": "test",
+            "omaSettings": [{
+                "isEncrypted": True,
+                "@odata.type": "#microsoft.graph.windows10OmaSetting",
+                "secretReferenceValueId": "0",
+                "omaUri": "test uri",
+                "displayName": "test",
+                "description": "",
+                "value": []}]}]}
+        self.oma_values = {
+            '@odata.context': 'https://graph.microsoft.com/beta/$metadata#Edm.String',
+            'value': 'password'}
+
+        self.makeapirequest.side_effect = self.profile, self.oma_values
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.token,
+            self.exclude)
+
+        self.assertTrue(Path(
+            f"{self.directory.path}/Device Configurations/test_windows10CustomConfiguration.json").exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_windows_custom_profile_not_encrypted(self):
+        """The file should be created and the count should be 1."""
+
+        self.profile = {'value': [{
+            "@odata.type": "#microsoft.graph.windows10CustomConfiguration",
+            "id": "0",
+            "displayName": "test",
+            "omaSettings": [{
+                "isEncrypted": False,
+                "@odata.type": "#microsoft.graph.windows10OmaSetting",
+                "secretReferenceValueId": "0",
+                "omaUri": "test uri",
+                "displayName": "test",
+                "description": "",
+                "value": []}]}]}
+        self.oma_values = {
+            '@odata.context': 'https://graph.microsoft.com/beta/$metadata#Edm.String',
+            'value': 'password'}
+
+        self.makeapirequest.side_effect = self.profile, self.oma_values
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.token,
+            self.exclude)
+
+        self.assertTrue(Path(
+            f"{self.directory.path}/Device Configurations/test_windows10CustomConfiguration.json").exists())
+        self.assertEqual(1, self.count)
+
+    def test_backup_non_custom_profile(self):
+        """The file should be created and the count should be 1."""
+
+        self.makeapirequest.return_value = {'value': [{
+            "@odata.type": '#microsoft.graph.macOSGeneralDeviceConfiguration',
+            "id": "0",
+            "displayName": "test"}]}
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.token,
+            self.exclude)
+
+        self.assertTrue(Path(
+            f"{self.directory.path}/Device Configurations/test_macOSGeneralDeviceConfiguration.json").exists())
+        self.assertEqual(1, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_remoteAssistancePartner.py b/tests/Backup/test_backup_remoteAssistancePartner.py
new file mode 100644
index 00000000..9ab718fc
--- /dev/null
+++ b/tests/Backup/test_backup_remoteAssistancePartner.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up remote assistance partner."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_remoteAssistancePartner import savebackup
+
+REMOTE_ASSISTANCE_PARTNER = {
+    "value": [{
+        "id": "0",
+        "onboardingStatus": "onboarded",
+        "displayName": "test"
+    }]}
+
+
+@patch("src.IntuneCD.backup_remoteAssistancePartner.savebackup")
+@patch("src.IntuneCD.backup_remoteAssistancePartner.makeapirequest",
+       return_value=REMOTE_ASSISTANCE_PARTNER)
+class TestBackupCompliancePartner(unittest.TestCase):
+    """Test class for backup_remoteAssistancePartner."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Partner Connections/Remote Assistance/test."
+        self.expected_data = {
+            "onboardingStatus": "onboarded",
+            "displayName": "test"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Partner Connections/Remote Assistance').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Partner Connections/Remote Assistance').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_onboarding_status_not_onboarded(
+            self, mock_data, mock_makeapirequest):
+        """The count should be 0 if the onboarding status is not onboarded."""
+
+        mock_data.return_value = {
+            "value": [{"onboardingStatus": "notOnboarded"}]}
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+    def test_backup_with_no_return_data(self, mock_data, mock_makeapirequest):
+        """The count should be 0 if no data is returned."""
+
+        mock_data.return_value = {"value": []}
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_shellScripts.py b/tests/Backup/test_backup_shellScripts.py
new file mode 100644
index 00000000..af9b3806
--- /dev/null
+++ b/tests/Backup/test_backup_shellScripts.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Shell Scripts."""
+
+import json
+import yaml
+import unittest
+
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_shellScripts import savebackup
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupShellScript(unittest.TestCase):
+    """Test class for backup_shellScripts."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/Scripts/Shell/test."
+        self.script_content_path = f"{self.directory.path}/Scripts/Shell/Script Data/test.ps1"
+        self.expected_data = {
+            'assignments': [
+                {
+                    'target': {
+                        'groupName': 'Group1'}}],
+            'displayName': 'test',
+            'fileName': 'test',
+            'scriptContent': 'WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE='}
+        self.script_policy_data = {
+            "value": [{"id": "0", "displayName": "test"}]}
+        self.batch_request_data = [
+            {
+                'id': '0',
+                'displayName': 'test',
+                'fileName': 'test',
+                'scriptContent': 'WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE='}]
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_shellScripts.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_shellScripts.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.batch_request_patch = patch(
+            'src.IntuneCD.backup_shellScripts.batch_request')
+        self.batch_request = self.batch_request_patch.start()
+        self.batch_request.return_value = self.batch_request_data
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_shellScripts.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.script_policy_data
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.batch_request.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'yaml',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(f'{self.directory.path}/Scripts/Shell')
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(f'{self.directory.path}/Scripts/Shell')
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_script_is_created(self):
+        """The folder should be created and a .ps1 file should be created."""
+
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertTrue(
+            f'{self.directory.path}/Scripts/Shell/Script Data')
+        self.assertTrue(self.script_content_path)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        self.batch_request.return_value = []
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_vppTokens.py b/tests/Backup/test_backup_vppTokens.py
new file mode 100644
index 00000000..ef088ee2
--- /dev/null
+++ b/tests/Backup/test_backup_vppTokens.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up VPP Tokens."""
+
+import json
+import yaml
+import unittest
+
+from pathlib import Path
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_vppTokens import savebackup
+
+VPP_TOKEN = {
+    "value": [{
+        "id": "0",
+        "tokenName": "test",
+        "displayName": "test"
+    }]}
+
+
+@patch("src.IntuneCD.backup_vppTokens.savebackup")
+@patch("src.IntuneCD.backup_vppTokens.makeapirequest",
+       return_value=VPP_TOKEN)
+class TestBackupVPPTokens(unittest.TestCase):
+    """Test class for backup_vppTokens."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.saved_path = f"{self.directory.path}/Apple VPP Tokens/test."
+        self.expected_data = {"tokenName": "test", "displayName": "test"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_backup_yml(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'yaml', self.token)
+
+        with open(self.saved_path + 'yaml', 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.saved_data = json.loads(data)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Apple VPP Tokens').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_json(self, mock_data, mock_makeapirequest):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        self.count = savebackup(self.directory.path, 'json', self.token)
+
+        with open(self.saved_path + 'json', 'r') as f:
+            self.saved_data = json.load(f)
+
+        self.assertTrue(
+            Path(f'{self.directory.path}/Apple VPP Tokens').exists())
+        self.assertEqual(self.expected_data, self.saved_data)
+        self.assertEqual(1, self.count)
+
+    def test_backup_with_no_return_data(self, mock_data, mock_makeapirequest):
+        """The count should be 0 if no data is returned."""
+
+        mock_data.return_value = {"value": []}
+        self.count = savebackup(self.directory.path, 'json', self.token)
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Backup/test_backup_windowsEnrollmentProfile.py b/tests/Backup/test_backup_windowsEnrollmentProfile.py
new file mode 100644
index 00000000..aeaa6e53
--- /dev/null
+++ b/tests/Backup/test_backup_windowsEnrollmentProfile.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+
+"""This module tests backing up Windows enrollment profile."""
+
+import json
+import yaml
+import unittest
+
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.backup_windowsEnrollmentProfile import savebackup
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {'groupName': 'Group1'}}]}]
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'Group1'}}]
+
+
+class TestBackupWindowsEnrollmentProfile(unittest.TestCase):
+    """Test class for backup_windowsEnrollmentProfile."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.token = 'token'
+        self.exclude = []
+        self.saved_path = f"{self.directory.path}/Enrollment Profiles/Windows/test."
+        self.expected_data = {'assignments': [{'target': {'groupName': 'Group1'}}], 'displayName': 'test'}
+        self.enrollment_profile = {
+            "value": [{
+                "displayName": "test",
+                "id": "0"
+            }]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.backup_windowsEnrollmentProfile.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.backup_windowsEnrollmentProfile.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.backup_windowsEnrollmentProfile.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.enrollment_profile
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+
+    def test_backup_yml(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        output = 'yaml'
+        count = savebackup(
+            self.directory.path,
+            output,
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + output, 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            saved_data = json.loads(data)
+
+        self.assertTrue(f'{self.directory.path}/Enrollment Profiles/Windows')
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, count)
+
+    def test_backup_json(self):
+        """The folder should be created, the file should have the expected contents, and the count should be 1."""
+
+        output = 'json'
+        count = savebackup(
+            self.directory.path,
+            output,
+            self.exclude,
+            self.token)
+
+        with open(self.saved_path + output, 'r') as f:
+            saved_data = json.load(f)
+
+        self.assertTrue(f'{self.directory.path}/Enrollment Profiles/Windows')
+        self.assertEqual(self.expected_data, saved_data)
+        self.assertEqual(1, count)
+
+    def test_backup_with_no_returned_data(self):
+        """The count should be 0 if no data is returned."""
+
+        self.makeapirequest.return_value = {'value': []}
+        self.count = savebackup(
+            self.directory.path,
+            'json',
+            self.exclude,
+            self.token)
+
+        self.assertEqual(0, self.count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_ManagementIntents.py b/tests/Update/test_update_ManagementIntents.py
new file mode 100644
index 00000000..9e8ed229
--- /dev/null
+++ b/tests/Update/test_update_ManagementIntents.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Management Intents."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_managementIntents import update
+
+
+class TestUpdateManagementIntents(unittest.TestCase):
+    """Test class for update_managementIntents."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Management Intents")
+        self.directory.write(
+            "Management Intents/macOS Firewall/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_data = {"value": [{"@odata.type": "test",
+                                    "id": "0",
+                                    "displayName": "test",
+                                    "assignments": [{"target": {"groupId": "test"}}]}]}
+        self.repo_data = {
+            "id": "0",
+            "displayName": "test",
+            "description": "Hi there",
+            "templateId": "0",
+            "settingsDelta": [
+                {
+                    "@odata.type": "#test.test",
+                    "definitionId": "test_test",
+                    "value": False
+                }],
+            "assignments": [{"target": {"groupName": "test1"}}]}
+        self.batch_intent_data = {"value": [{
+            "id": "0",
+            "displayName": "test",
+            "description": "Hi there",
+            "templateId": "0",
+            "settingsDelta": [
+                {
+                    "id": "0",
+                    "@odata.type": "#test.test",
+                    "definitionId": "test_test",
+                    "value": True
+                }]
+        }]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_managementIntents.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.batch_intents_patch = patch(
+            'src.IntuneCD.update_managementIntents.batch_intents')
+        self.batch_intents = self.batch_intents_patch.start()
+        self.batch_intents.return_value = self.batch_intent_data
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_managementIntents.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_managementIntents.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_managementIntents.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_managementIntents.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_managementIntents.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_managementIntents.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.batch_intents.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPost should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPost should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPost should not be called."""
+        self.batch_intent_data["value"][0]["settingsDelta"][0]['value'] = False
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should not be called."""
+
+        self.batch_intent_data["value"][0]["settingsDelta"][0]['value'] = False
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_data["value"][0]["displayName"] = "test1"
+        self.batch_intent_data["value"][0]["templateId"] = "test1_test1"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_appConfiguration.py b/tests/Update/test_update_appConfiguration.py
new file mode 100644
index 00000000..460d6b4d
--- /dev/null
+++ b/tests/Update/test_update_appConfiguration.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python3
+
+"""This module tests updating App Configurations."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_appConfiguration import update
+
+
+class TestUpdateAppConfiguration(unittest.TestCase):
+    """Test class for update_appConfiguration."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("App Configuration")
+        self.directory.write(
+            "App Configuration/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "App Configuration/test.txt", 'txt',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_data = {"value": [{"@odata.type": "test",
+                                    "id": "0",
+                                    "displayName": "test",
+                                    "testvalue": "test",
+                                    "assignments": [{"target": {"groupId": "test"}}]}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "displayName": "test",
+                          "testvalue": "test1",
+                          "targetedMobileApps": {"appName": "test",
+                                                 "type": "#test.test.test"},
+                          "assignments": [{"target": {"groupName": "test1"}}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_appConfiguration.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_appConfiguration.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_appConfiguration.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_appConfiguration.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_appConfiguration.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_appConfiguration.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_appConfiguration.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_appConfiguration.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.mem_data["value"][0]["testvalue"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data["value"][0]["testvalue"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_data["value"][0]["displayName"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_config_not_found_and_app_not_found(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should not be called."""
+
+        self.mem_data["value"][0]["displayName"] = "test1"
+        self.repo_data["targetedMobileApps"] = {}
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_appProtection.py b/tests/Update/test_update_appProtection.py
new file mode 100644
index 00000000..dabb9f4e
--- /dev/null
+++ b/tests/Update/test_update_appProtection.py
@@ -0,0 +1,407 @@
+#!/usr/bin/env python3
+
+"""This module tests updating App Protections."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_appProtection import update
+
+
+BATCH_ASSIGNMENT = [
+    {
+        'value': [
+            {
+                'id': '0',
+                'target': {
+                    'groupName': 'test1'}}]}]
+
+OBJECT_ASSIGNMENT = [
+    {
+        'target': {
+            'groupName': 'test'}}]
+
+UPDATE_ASSIGNMENT = {"assignments": [{"target": {"groupName": "test1"}}]}
+
+
+class TestUpdateAppProtection(unittest.TestCase):
+    """Test class for update_appProtection."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("App Protection")
+        self.directory.write(
+            "App Protection/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "App Protection/test.txt", 'txt',
+            encoding='utf-8')
+        self.token = 'token'
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_appProtection.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+        self.batch_assignment.return_value = BATCH_ASSIGNMENT
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_appProtection.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+        self.object_assignment.return_value = OBJECT_ASSIGNMENT
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_appProtection.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = {
+            "value": [
+                {
+                    "@odata.type": "#test.test.test",
+                    "id": "0",
+                    "displayName": "test",
+                    "testvalue": "test",
+                    "targetedAppManagementLevels": "test",
+                    "assignments": [
+                        {
+                            "target": {
+                                "groupId": "test"}}]}]}
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_appProtection.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+        self.update_assignment.return_value = UPDATE_ASSIGNMENT
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_appProtection.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = {
+            "@odata.type": "#test.test.test",
+            "id": "0",
+            "displayName": "test",
+            "targetedAppManagementLevels": "test",
+            "testvalue": "test1",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_appProtection.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_appProtection.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_appProtection.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.load_file.return_value = {
+            "@odata.type": "#test.test.test",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.load_file.return_value = {
+            "@odata.type": "#test.test.test",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_mdmWindowsInformationProtectionPolicy_with_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.makeapirequest.return_value = {"value": [{
+            "@odata.type": "#microsoft.graph.mdmWindowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}]}
+        self.load_file.return_value = {
+            "@odata.type": "#microsoft.graph.mdmWindowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test1",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_mdmWindowsInformationProtectionPolicy_with_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.makeapirequest.return_value = {"value": [{
+            "@odata.type": "#microsoft.graph.mdmWindowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}]}
+        self.load_file.return_value = {
+            "@odata.type": "#microsoft.graph.mdmWindowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test1",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_mdmWindowsInformationProtectionPolicy_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.makeapirequest.return_value = {"value": [{
+            "@odata.type": "#microsoft.graph.mdmWindowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}]}
+        self.load_file.return_value = {
+            "@odata.type": "#microsoft.graph.mdmWindowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_mdmWindowsInformationProtectionPolicy_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.makeapirequest.return_value = {"value": [{
+            "@odata.type": "#microsoft.graph.mdmWindowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}]}
+        self.load_file.return_value = {
+            "@odata.type": "#microsoft.graph.mdmWindowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_windowsInformationProtectionPolicy_with_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.makeapirequest.return_value = {"value": [{
+            "@odata.type": "#microsoft.graph.windowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}]}
+        self.load_file.return_value = {
+            "@odata.type": "#microsoft.graph.windowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test1",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_windowsInformationProtectionPolicy_with_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.makeapirequest.return_value = {"value": [{
+            "@odata.type": "#microsoft.graph.windowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}]}
+        self.load_file.return_value = {
+            "@odata.type": "#microsoft.graph.windowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test1",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_windowsInformationProtectionPolicy_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.makeapirequest.return_value = {"value": [{
+            "@odata.type": "#microsoft.graph.windowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}]}
+        self.load_file.return_value = {
+            "@odata.type": "#microsoft.graph.windowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_windowsInformationProtectionPolicy_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.makeapirequest.return_value = {"value": [{
+            "@odata.type": "#microsoft.graph.windowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}]}
+        self.load_file.return_value = {
+            "@odata.type": "#microsoft.graph.windowsInformationProtectionPolicy",
+            "id": "0",
+            "displayName": "test",
+            "testvalue": "test",
+            "assignments": [
+                {
+                    "target": {
+                        "groupName": "test1"}}]}
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.makeapirequest.return_value = {"value": [
+            {"@odata.type": "#test.test.test", "id": "0", "displayName": "test1"}]}
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_appleEnrollmentProfile.py b/tests/Update/test_update_appleEnrollmentProfile.py
new file mode 100644
index 00000000..a26f0f4e
--- /dev/null
+++ b/tests/Update/test_update_appleEnrollmentProfile.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Apple Enrollment Profiles."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_appleEnrollmentProfile import update
+
+
+class TestUpdateAppleEnrollmentProfile(unittest.TestCase):
+    """Test class for update_appleEnrollmentProfile."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Enrollment Profiles/Apple")
+        self.directory.write(
+            "Enrollment Profiles/Apple/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Enrollment Profiles/Apple/test.txt", '{"test": "test"}',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_data = {"value": [{"@odata.type": "test",
+                                    "id": "0",
+                                    "displayName": "test",
+                                    "testvalue": "test"}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "displayName": "test",
+                          "testvalue": "test1"}
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_appleEnrollmentProfile.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_appleEnrollmentProfile.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_appleEnrollmentProfile.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.makeapirequest_patch.stop()
+        self.load_file_patch.stop()
+        self.makeapirequestPatch_patch.stop()
+
+    def test_update_with_diffs(self):
+        """The count should be 1 and makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+
+    def test_update_with_multiple_diffs(self):
+        """The count should be 1 and makeapirequestPatch should be called."""
+
+        self.repo_data['testvalue2'] = 'test2'
+        self.mem_data['value'][0]['testvalue'] = 'test'
+        self.mem_data['value'][0]['testvalue2'] = 'test1'
+
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+
+    def test_update_with_no_diffs(self):
+        """The count should be 0 and makeapirequestPatch should not be called."""
+
+        self.mem_data['value'][0]['testvalue'] = 'test1'
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_assignment.py b/tests/Update/test_update_assignment.py
new file mode 100644
index 00000000..f55232a6
--- /dev/null
+++ b/tests/Update/test_update_assignment.py
@@ -0,0 +1,65 @@
+import unittest
+
+from unittest.mock import patch
+from src.IntuneCD.update_assignment import update_assignment, get_added_removed, post_assignment_update
+
+
+class TestUpdateAssignment(unittest.TestCase):
+    """Test class for update_assignment."""
+
+    def setUp(self):
+        self.token = 'token'
+        self.output_data = {
+            'assignments': {
+                'intent': 'apply',
+                'target': {
+                    '@odata.type': '#microsoft.graph.allDevicesAssignmentTarget',
+                    'deviceAndAppManagementAssignmentFilterId': "1234",
+                    'deviceAndAppManagementAssignmentFilterType': 'device'}}}
+        self.mem_data = []
+        self.repo_data = [
+            {
+                'source': 'direct',
+                'intent': 'apply',
+                'target': {
+                    '@odata.type': '#microsoft.graph.groupAssignmentTarget',
+                    'deviceAndAppManagementAssignmentFilterId': 'test',
+                    'deviceAndAppManagementAssignmentFilterType': 'device',
+                    'groupName': 'test'}}]
+        self.request_data = {'value': [{'id': '12345', 'displayName': 'test'}]}
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_assignment.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_assignment.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+
+    def tearDown(self):
+        self.makeapirequest.stop()
+        self.makeapirequestPost.stop()
+
+    def test_get_added_removed(self):
+        """The list of added and removed should be returned."""
+        result = get_added_removed(self.output_data)
+
+        self.assertEqual(
+            result,
+            ['intent: apply, Filter ID: 1234, Filter Type: device, target: All Devices'])
+
+    def test_update_assignment(self):
+
+        self.makeapirequest.return_value = self.request_data
+
+        result = update_assignment(
+            self.repo_data, self.mem_data, self.token)
+
+        self.assertEqual(result,
+                         [{'source': 'direct',
+                           'intent': 'apply',
+                           'target': {'@odata.type': '#microsoft.graph.groupAssignmentTarget',
+                                      'deviceAndAppManagementAssignmentFilterId': '12345',
+                                      'deviceAndAppManagementAssignmentFilterType': 'device',
+                                      'groupId': '12345'}}])
+
+        self.assertEqual(self.makeapirequest.call_count, 2)
diff --git a/tests/Update/test_update_assignmentFilter.py b/tests/Update/test_update_assignmentFilter.py
new file mode 100644
index 00000000..142e1ab2
--- /dev/null
+++ b/tests/Update/test_update_assignmentFilter.py
@@ -0,0 +1,100 @@
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_assignmentFilter import update
+
+
+class TestUpdateAssignmentFilter(unittest.TestCase):
+    """Test class for update_assignmentFilter."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Filters")
+        self.directory.write(
+            "Filters/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Filters/test.txt", '{"test": "test"}',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_data = {"value": [{"@odata.type": "test",
+                                    "id": "0",
+                                    "displayName": "test",
+                                    "testvalue": "test"}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "displayName": "test",
+                          "testvalue": "test1"}
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_assignmentFilter.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_assignmentFilter.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_assignmentFilter.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_assignmentFilter.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {'id': '0'}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.makeapirequest_patch.stop()
+        self.load_file_patch.stop()
+        self.makeapirequestPatch_patch.stop()
+        self.makeapirequestPost_patch.stop()
+
+    def test_update_with_diffs(self):
+        """The count should be 1 and makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+
+    def test_update_with_multiple_diffs(self):
+        """The count should be 1 and makeapirequestPatch should be called."""
+
+        self.repo_data['testvalue2'] = 'test2'
+        self.mem_data['value'][0]['testvalue'] = 'test'
+        self.mem_data['value'][0]['testvalue2'] = 'test1'
+
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+
+    def test_update_with_no_diffs(self):
+        """The count should be 0 and makeapirequestPatch should not be called."""
+
+        self.mem_data['value'][0]['testvalue'] = 'test1'
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+
+    def test_update_config_not_found(self):
+        """The count should be 0 and makeapirequestPost should be called."""
+
+        self.mem_data['value'][0]['displayName'] = 'test1'
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_compliance.py b/tests/Update/test_update_compliance.py
new file mode 100644
index 00000000..0c4455ac
--- /dev/null
+++ b/tests/Update/test_update_compliance.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Compliance."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_compliance import update
+
+
+class TestUpdateCompliance(unittest.TestCase):
+    """Test class for update_compliance."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Compliance Policies")
+        self.directory.write(
+            "Compliance Policies/Policies/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Compliance Policies/Policies/test.txt", 'txt',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_data = {"value": [{"@odata.type": "test",
+                                    "id": "0",
+                                    "displayName": "test",
+                                    "testvalue": "test",
+                                    "scheduledActionsForRule": [{"scheduledActionConfigurations": [{"gracePeriodHours": 0}]}],
+                                    "assignments": [{"target": {"groupId": "test"}}]}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "displayName": "test",
+                          "testvalue": "test1",
+                          "scheduledActionsForRule": [{"scheduledActionConfigurations": [{"gracePeriodHours": 1}]}],
+                          "assignments": [{"target": {"groupName": "test1"}}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_compliance.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_compliance.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_compliance.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_compliance.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_compliance.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_compliance.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_compliance.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_compliance.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 2 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 2 and the makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.mem_data["value"][0]["testvalue"] = "test1"
+        self.mem_data["value"][0]["scheduledActionsForRule"][0]["scheduledActionConfigurations"][0]["gracePeriodHours"] = 1
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data["value"][0]["testvalue"] = "test1"
+        self.mem_data["value"][0]["scheduledActionsForRule"][0]["scheduledActionConfigurations"][0][
+            "gracePeriodHours"] = 1
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_data["value"][0]["displayName"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_configurationPolicies.py b/tests/Update/test_update_configurationPolicies.py
new file mode 100644
index 00000000..dd1f2e96
--- /dev/null
+++ b/tests/Update/test_update_configurationPolicies.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Configuration Policies."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_configurationPolicies import update
+
+
+class TestUpdateConfigurationPolicies(unittest.TestCase):
+    """Test class for update_configurationPolicies."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Settings Catalog")
+        self.directory.write(
+            "Settings Catalog/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Settings Catalog/test.txt", 'txt',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_data = {"value": [{"@odata.type": "test",
+                                    "id": "0",
+                                    "name": "test",
+                                    "testvalue": "test",
+                                    "settings": [{"id": "0",
+                                                  "testvalue": "test"}],
+                                    "assignments": [{"target": {"groupId": "test"}}]}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "name": "test",
+                          "testvalue": "test1",
+                          "settings": [
+                              {
+                                  "id": "0",
+                                  "testvalue": "test1"}],
+                          "assignments": [{"target": {"groupName": "test1"}}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_configurationPolicies.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_configurationPolicies.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_configurationPolicies.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_configurationPolicies.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_configurationPolicies.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_configurationPolicies.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_configurationPolicies.makeapirequestPut')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_configurationPolicies.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.mem_data["value"][0]["testvalue"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data["value"][0]["testvalue"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_data["value"][0]["name"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_notificationTemplate.py b/tests/Update/test_update_notificationTemplate.py
new file mode 100644
index 00000000..e8953279
--- /dev/null
+++ b/tests/Update/test_update_notificationTemplate.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Notification Templates."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_notificationTemplate import update
+
+
+class TestUpdateNotificationTemplates(unittest.TestCase):
+    """Test class for update_notificationTemplate."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Compliance Policies/Message Templates")
+        self.directory.write(
+            "Compliance Policies/Message Templates/test.json",
+            '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Compliance Policies/Message Templates/test.txt",
+            '{"test": "test"}',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_data = {"value": [{
+            "id": "0",
+            "displayName": "test",
+            "defaultLocale": "en-us",
+            "brandingOptions": "test",
+            "roleScopeTagIds": "[0]",
+            "localizedNotificationMessages": [{"messageTemplate": "test"}]}]}
+        self.mem_template_data = {
+            "id": "0",
+            "displayName": "test",
+            "defaultLocale": "en-us",
+            "brandingOptions": "test",
+            "roleScopeTagIds": "[0]",
+            "localizedNotificationMessages": [
+                {
+                    "messageTemplate": "test",
+                    'lastModifiedDateTime': 'test',
+                    "locale": "en-us",
+                    "id": "0"}]}
+        self.repo_data = {
+            "displayName": "test",
+            "defaultLocale": "en-us",
+            "brandingOptions": "test",
+            "roleScopeTagIds": "[0]",
+            "localizedNotificationMessages": [{"messageTemplate": "test1", "locale": "en-us"}]}
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_notificationTemplate.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_notificationTemplate.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_notificationTemplate.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_notificationTemplate.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.makeapirequest_patch.stop()
+        self.load_file_patch.stop()
+        self.makeapirequestPatch_patch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs(self):
+        """The count should be 1 and makeapirequestPatch should be called."""
+
+        self.makeapirequest.side_effect = [
+            self.mem_data, self.mem_template_data]
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+
+    def test_update_with_multiple_diffs(self):
+        """The count should be 2 and makeapirequestPatch should be called."""
+
+        self.repo_data['brandingOptions'] = 'test1'
+        self.makeapirequest.side_effect = [
+            self.mem_data, self.mem_template_data]
+
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 2)
+
+    def test_update_with_no_diffs(self):
+        """The count should be 0 and makeapirequestPatch should not be called."""
+
+        self.mem_template_data['localizedNotificationMessages'][0]['messageTemplate'] = 'test1'
+        self.makeapirequest.side_effect = [
+            self.mem_data, self.mem_template_data]
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+
+    def test_update_config_not_found(self):
+        """The count should be 0 and makeapirequestPost should be called."""
+
+        self.mem_data['value'][0]['displayName'] = 'test1'
+        self.count = update(self.directory.path, self.token)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 2)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_powershellScripts.py b/tests/Update/test_update_powershellScripts.py
new file mode 100644
index 00000000..9ff0efcb
--- /dev/null
+++ b/tests/Update/test_update_powershellScripts.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Powershell Scripts."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_powershellScripts import update
+
+
+class TestUpdatePowershellScripts(unittest.TestCase):
+    """Test class for update_powershellScripts."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Scripts/Powershell")
+        self.directory.makedir("Scripts/Powershell/Script Data")
+        self.directory.write(
+            "Scripts/Powershell/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Scripts/Powershell/Script Data/test.ps1",
+            'You found a secret message, hooray!',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_script_content = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2Us"
+        self.repo_script_content = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE="
+        self.mem_powershellScript_data = {
+            "value": [
+                {
+                    "@odata.type": "test",
+                    "id": "0",
+                    "displayName": "test",
+                    "testvalue": "test",
+                    "scriptContent": self.mem_script_content,
+                    "fileName": "test.ps1",
+                    "assignments": [
+                        {
+                            "target": {
+                                "groupId": "test"}}]}]}
+        self.mem_data = {"@odata.type": "test",
+                         "id": "0",
+                         "displayName": "test",
+                         "testvalue": "test",
+                         "scriptContent": self.mem_script_content,
+                         "fileName": "test.ps1",
+                         "assignments": [{"target": {"groupId": "test"}}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "displayName": "test",
+                          "testvalue": "test",
+                          "scriptContent": self.repo_script_content,
+                          "fileName": "test.ps1",
+                          "assignments": [{"target": {"groupId": "test"}}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_powershellScripts.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_powershellScripts.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_powershellScripts.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_powershellScripts.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_powershellScripts.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_powershellScripts.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_powershellScripts.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_powershellScripts.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.repo_data['testvalue'] = "test1"
+        self.makeapirequest.side_effect = [
+            self.mem_powershellScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.repo_data['testvalue'] = "test1"
+        self.makeapirequest.side_effect = [
+            self.mem_powershellScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.mem_data['testvalue'] = "test"
+        self.mem_data['scriptContent'] = self.repo_script_content
+        self.makeapirequest.side_effect = [
+            self.mem_powershellScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data['testvalue'] = "test"
+        self.mem_data['scriptContent'] = self.repo_script_content
+        self.makeapirequest.side_effect = [
+            self.mem_powershellScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_powershellScript_data["value"][0]["displayName"] = "test1"
+        self.makeapirequest.return_value = self.mem_powershellScript_data
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_proactiveRemediation.py b/tests/Update/test_update_proactiveRemediation.py
new file mode 100644
index 00000000..b41f5b15
--- /dev/null
+++ b/tests/Update/test_update_proactiveRemediation.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Proactive Remediation."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_proactiveRemediation import update
+
+
+class TestUpdateProactiveRemediation(unittest.TestCase):
+    """Test class for update_proactiveRemediation."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Proactive Remediations")
+        self.directory.makedir("Proactive Remediations/Script Data")
+        self.directory.write(
+            "Proactive Remediations/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Proactive Remediations/Script Data/test_DetectionScript.ps1",
+            'You found a secret message, hooray!',
+            encoding='utf-8')
+        self.directory.write(
+            "Proactive Remediations/Script Data/test_RemediationScript.ps1",
+            'You found a secret message, hooray!',
+            encoding='utf-8')
+        self.token = 'token'
+        self.script_content = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE="
+        self.mem_remediationScript_data = {
+            "value": [
+                {
+                    "@odata.type": "test",
+                    "id": "0",
+                    "displayName": "test",
+                    "testvalue": "test",
+                    "detectionScriptContent": self.script_content,
+                    "remediationScriptContent": self.script_content,
+                    "fileName": "test.ps1",
+                    "assignments": [
+                        {
+                            "target": {
+                                "groupId": "test"}}]}]}
+        self.mem_data = {"@odata.type": "test",
+                         "id": "0",
+                         "displayName": "test",
+                         "testvalue": "test",
+                         "detectionScriptContent": self.script_content,
+                         "remediationScriptContent": self.script_content,
+                         "fileName": "test.ps1",
+                         "assignments": [{"target": {"groupId": "test"}}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "displayName": "test",
+                          "testvalue": "test",
+                          "detectionScriptContent": self.script_content,
+                          "remediationScriptContent": self.script_content,
+                          "fileName": "test.ps1",
+                          "assignments": [{"target": {"groupId": "test"}}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_proactiveRemediation.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_proactiveRemediation.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_proactiveRemediation.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_proactiveRemediation.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_proactiveRemediation.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_proactiveRemediation.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_proactiveRemediation.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_proactiveRemediation.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.repo_data['testvalue'] = "test1"
+        self.makeapirequest.side_effect = [
+            self.mem_remediationScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_detection_script_diff_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.mem_data['detectionScriptContent'] = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheQ=="
+        self.makeapirequest.side_effect = [
+            self.mem_remediationScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_detection_script_diff_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.mem_data['detectionScriptContent'] = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheQ=="
+        self.makeapirequest.side_effect = [
+            self.mem_remediationScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_remediation_script_diff_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.mem_data['remediationScriptContent'] = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheQ=="
+        self.makeapirequest.side_effect = [
+            self.mem_remediationScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_remediation_script_diff_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.mem_data['remediationScriptContent'] = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheQ=="
+        self.makeapirequest.side_effect = [
+            self.mem_remediationScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.repo_data['testvalue'] = "test1"
+        self.makeapirequest.side_effect = [
+            self.mem_remediationScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.repo_data['testvalue'] = "test"
+        self.makeapirequest.side_effect = [
+            self.mem_remediationScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data['testvalue'] = "test"
+        self.makeapirequest.side_effect = [
+            self.mem_remediationScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_remediationScript_data["value"][0]["displayName"] = "test1"
+        self.makeapirequest.return_value = self.mem_remediationScript_data
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_profiles.py b/tests/Update/test_update_profiles.py
new file mode 100644
index 00000000..49b3b83c
--- /dev/null
+++ b/tests/Update/test_update_profiles.py
@@ -0,0 +1,518 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Profiles."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_profiles import update
+
+
+class TestUpdateCompliance(unittest.TestCase):
+    """Test class for update_profiles."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Device Configurations")
+        self.directory.write(
+            "Device Configurations/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Device Configurations/mobileconfig/test.mobileconfig",
+            "test",
+            encoding='utf - 8')
+        self.token = 'token'
+        self.mem_data_base = {"value": [{"@odata.type": "test",
+                                         "id": "0",
+                                         "displayName": "test",
+                                         "testvalue": "test",
+                                         "assignments": [{"target": {"groupId": "test"}}]}]}
+        self.repo_data_base = {"@odata.type": "test",
+                               "id": "0",
+                               "displayName": "test",
+                               "testvalue": "test1",
+                               "assignments": [{"target": {"groupName": "test1"}}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_profiles.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_profiles.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_profiles.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data_base
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_profiles.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_profiles.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data_base
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_profiles.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_profiles.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_profiles.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+        self.plistlib_patch = patch('src.IntuneCD.update_profiles.plistlib')
+        self.plistlib = self.plistlib_patch.start()
+        self.plistlib.load.return_value = {
+            'PayloadContent': [{'test': 'test'}]}
+        self.plistlib.dumps.return_value = b'test'
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+        self.plistlib.stop()
+
+    def test_update_custom_macOS_with_diffs_and_assignment(self):
+        """The count should be 2 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.repo_payload = {'PayloadContent': [{'test': 'test1'}]}
+        self.mem_payload = {'PayloadContent': [{'test': 'test'}]}
+
+        self.plistlib.load.side_effect = self.repo_payload, self.mem_payload
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.macOSCustomConfiguration"
+        self.mem_data_base["value"][0]["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.mem_data_base["value"][0]["payloadFileName"] = "test.mobileconfig"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.macOSCustomConfiguration"
+        self.repo_data_base["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.repo_data_base["payloadFileName"] = "test.mobileconfig"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_custom_macOS_with_diffs_no_assignment(self):
+        """The count should be 2 and the makeapirequestPatch should be called."""
+
+        self.repo_payload = {'PayloadContent': [{'test': 'test1'}]}
+        self.mem_payload = {'PayloadContent': [{'test': 'test'}]}
+
+        self.plistlib.load.side_effect = self.repo_payload, self.mem_payload
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.macOSCustomConfiguration"
+        self.mem_data_base["value"][0]["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.mem_data_base["value"][0]["payloadFileName"] = "test.mobileconfig"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.macOSCustomConfiguration"
+        self.repo_data_base["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.repo_data_base["payloadFileName"] = "test.mobileconfig"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_custom_macOS_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.repo_payload = {'PayloadContent': [{'test': 'test'}]}
+        self.mem_payload = {'PayloadContent': [{'test': 'test'}]}
+        self.repo_data_base["testvalue"] = "test"
+
+        self.plistlib.load.side_effect = self.repo_payload, self.mem_payload
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.macOSCustomConfiguration"
+        self.mem_data_base["value"][0]["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.mem_data_base["value"][0]["payloadFileName"] = "test.mobileconfig"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.macOSCustomConfiguration"
+        self.repo_data_base["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.repo_data_base["payloadFileName"] = "test.mobileconfig"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_custom_macOS_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.repo_payload = {'PayloadContent': [{'test': 'test'}]}
+        self.mem_payload = {'PayloadContent': [{'test': 'test'}]}
+        self.repo_data_base["testvalue"] = "test"
+
+        self.plistlib.load.side_effect = self.repo_payload, self.mem_payload
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.macOSCustomConfiguration"
+        self.mem_data_base["value"][0]["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.mem_data_base["value"][0]["payloadFileName"] = "test.mobileconfig"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.macOSCustomConfiguration"
+        self.repo_data_base["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.repo_data_base["payloadFileName"] = "test.mobileconfig"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_custom_iOS_with_diffs_and_assignment(self):
+        """The count should be 2 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.repo_payload = {'PayloadContent': [{'test': 'test1'}]}
+        self.mem_payload = {'PayloadContent': [{'test': 'test'}]}
+
+        self.plistlib.load.side_effect = self.repo_payload, self.mem_payload
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.iosCustomConfiguration"
+        self.mem_data_base["value"][0]["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.mem_data_base["value"][0]["payloadFileName"] = "test.mobileconfig"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.iosCustomConfiguration"
+        self.repo_data_base["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.repo_data_base["payloadFileName"] = "test.mobileconfig"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_custom_iOS_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.repo_payload = {'PayloadContent': [{'test': 'test1'}]}
+        self.mem_payload = {'PayloadContent': [{'test': 'test'}]}
+
+        self.plistlib.load.side_effect = self.repo_payload, self.mem_payload
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.iosCustomConfiguration"
+        self.mem_data_base["value"][0]["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.mem_data_base["value"][0]["payloadFileName"] = "test.mobileconfig"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.iosCustomConfiguration"
+        self.repo_data_base["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.repo_data_base["payloadFileName"] = "test.mobileconfig"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_custom_iOS_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.repo_payload = {'PayloadContent': [{'test': 'test'}]}
+        self.mem_payload = {'PayloadContent': [{'test': 'test'}]}
+        self.repo_data_base["testvalue"] = "test"
+
+        self.plistlib.load.side_effect = self.repo_payload, self.mem_payload
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.iosCustomConfiguration"
+        self.mem_data_base["value"][0]["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.mem_data_base["value"][0]["payloadFileName"] = "test.mobileconfig"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.iosCustomConfiguration"
+        self.repo_data_base["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.repo_data_base["payloadFileName"] = "test.mobileconfig"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_custom_iOS_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.repo_payload = {'PayloadContent': [{'test': 'test'}]}
+        self.mem_payload = {'PayloadContent': [{'test': 'test'}]}
+        self.repo_data_base["testvalue"] = "test"
+
+        self.plistlib.load.side_effect = self.repo_payload, self.mem_payload
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.iosCustomConfiguration"
+        self.mem_data_base["value"][0]["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.mem_data_base["value"][0]["payloadFileName"] = "test.mobileconfig"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.iosCustomConfiguration"
+        self.repo_data_base["payload"] = "T29vIHllYWgsIGl0J3MgZ29ubmEgd29yaywgSSBwcm9taXNl"
+        self.repo_data_base["payloadFileName"] = "test.mobileconfig"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_custom_windows_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.mem_data_base["value"][0]["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {}}]
+
+        self.oma_values = {
+            '@odata.context': 'https://graph.microsoft.com/beta/$metadata#Edm.String',
+            'value': 'password'}
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.repo_data_base["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {
+                "@odata.context": "https://graph.microsoft.com/beta/$metadata#Edm.String",
+                "value": "password2"
+            }}]
+
+        self.makeapirequest.side_effect = self.mem_data_base, self.oma_values
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_custom_windows_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.mem_data_base["value"][0]["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {}}]
+
+        self.oma_values = {
+            '@odata.context': 'https://graph.microsoft.com/beta/$metadata#Edm.String',
+            'value': 'password'}
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.repo_data_base["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {
+                "@odata.context": "https://graph.microsoft.com/beta/$metadata#Edm.String",
+                "value": "password2"
+            }}]
+
+        self.makeapirequest.side_effect = self.mem_data_base, self.oma_values
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_custom_windows_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.mem_data_base["value"][0]["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {}}]
+
+        self.oma_values = {
+            '@odata.context': 'https://graph.microsoft.com/beta/$metadata#Edm.String',
+            'value': 'password'}
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.repo_data_base["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {
+                "@odata.context": "https://graph.microsoft.com/beta/$metadata#Edm.String",
+                "value": "password"
+            }}]
+
+        self.makeapirequest.side_effect = self.mem_data_base, self.oma_values
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_custom_windows_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.mem_data_base["value"][0]["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {}}]
+
+        self.oma_values = {
+            '@odata.context': 'https://graph.microsoft.com/beta/$metadata#Edm.String',
+            'value': 'password'}
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.repo_data_base["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {
+                "@odata.context": "https://graph.microsoft.com/beta/$metadata#Edm.String",
+                "value": "password"
+            }}]
+
+        self.makeapirequest.side_effect = self.mem_data_base, self.oma_values
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_other_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.macOSConfiguration"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.macOSConfiguration"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_other_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.macOSConfiguration"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.macOSConfiguration"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_other_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.macOSConfiguration"
+        self.mem_data_base["value"][0]["testvalue"] = "test1"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.macOSConfiguration"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_other_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data_base["value"][0]["@odata.type"] = "#microsoft.graph.macOSConfiguration"
+        self.mem_data_base["value"][0]["testvalue"] = "test1"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.macOSConfiguration"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_data_base["value"][0]["displayName"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_custom_windows_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_data_base["value"][0]["displayName"] = "test1"
+
+        self.repo_data_base["@odata.type"] = "#microsoft.graph.windows10CustomConfiguration"
+        self.repo_data_base["omaSettings"] = [{
+            "isEncrypted": True,
+            "@odata.type": "#microsoft.graph.windows10OmaSetting",
+            "secretReferenceValueId": "0",
+            "omaUri": "test uri",
+            "displayName": "test",
+            "description": "",
+            "value": {
+                "@odata.context": "https://graph.microsoft.com/beta/$metadata#Edm.String",
+                "value": "password2"
+            }}]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_shellScripts.py b/tests/Update/test_update_shellScripts.py
new file mode 100644
index 00000000..5386ddd7
--- /dev/null
+++ b/tests/Update/test_update_shellScripts.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Shell Scripts."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_shellScripts import update
+
+
+class TestUpdateShellScripts(unittest.TestCase):
+    """Test class for update_shellScripts."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Scripts/Shell")
+        self.directory.makedir("Scripts/Shell/Script Data")
+        self.directory.write(
+            "Scripts/Shell/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.directory.write(
+            "Scripts/Shell/Script Data/test.sh",
+            'You found a secret message, hooray!',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_script_content = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2Us"
+        self.repo_script_content = "WW91IGZvdW5kIGEgc2VjcmV0IG1lc3NhZ2UsIGhvb3JheSE="
+        self.mem_shellScript_data = {
+            "value": [
+                {
+                    "@odata.type": "test",
+                    "id": "0",
+                    "displayName": "test",
+                    "testvalue": "test",
+                    "scriptContent": self.mem_script_content,
+                    "fileName": "test.sh",
+                    "assignments": [
+                        {
+                            "target": {
+                                "groupId": "test"}}]}]}
+        self.mem_data = {"@odata.type": "test",
+                         "id": "0",
+                         "displayName": "test",
+                         "testvalue": "test",
+                         "scriptContent": self.mem_script_content,
+                         "fileName": "test.sh",
+                         "assignments": [{"target": {"groupId": "test"}}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "displayName": "test",
+                          "testvalue": "test",
+                          "scriptContent": self.repo_script_content,
+                          "fileName": "test.sh",
+                          "assignments": [{"target": {"groupId": "test"}}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_shellScripts.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_shellScripts.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_shellScripts.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_shellScripts.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_shellScripts.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_shellScripts.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_shellScripts.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_shellScripts.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.repo_data['testvalue'] = "test1"
+        self.makeapirequest.side_effect = [
+            self.mem_shellScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.repo_data['testvalue'] = "test1"
+        self.makeapirequest.side_effect = [
+            self.mem_shellScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 2)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.mem_data['testvalue'] = "test"
+        self.mem_data['scriptContent'] = self.repo_script_content
+
+        self.makeapirequest.side_effect = [
+            self.mem_shellScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data['testvalue'] = "test"
+        self.mem_data['scriptContent'] = self.repo_script_content
+
+        self.makeapirequest.side_effect = [
+            self.mem_shellScript_data, self.mem_data]
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_shellScript_data["value"][0]["displayName"] = "test1"
+        self.makeapirequest.return_value = self.mem_shellScript_data
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/Update/test_update_windowsEnrollmentProfile.py b/tests/Update/test_update_windowsEnrollmentProfile.py
new file mode 100644
index 00000000..92e600db
--- /dev/null
+++ b/tests/Update/test_update_windowsEnrollmentProfile.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+
+"""This module tests updating Windows Enrollment Profiles."""
+
+import unittest
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.update_windowsEnrollmentProfile import update
+
+
+class TestUpdateWindowsEnrollmentProfile(unittest.TestCase):
+    """Test class for update_windowsEnrollmentProfile."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("Enrollment Profiles/Windows")
+        self.directory.write(
+            "Enrollment Profiles/Windows/test.json", '{"test": "test"}',
+            encoding='utf-8')
+        self.token = 'token'
+        self.mem_data = {"value": [{"@odata.type": "test",
+                                    "id": "0",
+                                    "displayName": "test",
+                                    "testvalue": "test",
+                                    "assignments": [{"target": {"groupId": "test"}}]}]}
+        self.repo_data = {"@odata.type": "test",
+                          "id": "0",
+                          "displayName": "test",
+                          "testvalue": "test1",
+                          "managementServiceAppId": "0",
+                          "assignments": [{"target": {"groupName": "test1"}}]}
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.update_windowsEnrollmentProfile.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.object_assignment_patch = patch(
+            'src.IntuneCD.update_windowsEnrollmentProfile.get_object_assignment')
+        self.object_assignment = self.object_assignment_patch.start()
+
+        self.makeapirequest_patch = patch(
+            'src.IntuneCD.update_windowsEnrollmentProfile.makeapirequest')
+        self.makeapirequest = self.makeapirequest_patch.start()
+        self.makeapirequest.return_value = self.mem_data
+
+        self.update_assignment_patch = patch(
+            'src.IntuneCD.update_windowsEnrollmentProfile.update_assignment')
+        self.update_assignment = self.update_assignment_patch.start()
+
+        self.load_file_patch = patch(
+            'src.IntuneCD.update_windowsEnrollmentProfile.load_file')
+        self.load_file = self.load_file_patch.start()
+        self.load_file.return_value = self.repo_data
+
+        self.post_assignment_update_patch = patch(
+            'src.IntuneCD.update_windowsEnrollmentProfile.post_assignment_update')
+        self.post_assignment_update = self.post_assignment_update_patch.start()
+
+        self.makeapirequestPatch_patch = patch(
+            'src.IntuneCD.update_windowsEnrollmentProfile.makeapirequestPatch')
+        self.makeapirequestPatch = self.makeapirequestPatch_patch.start()
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.update_windowsEnrollmentProfile.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+        self.makeapirequestPost.return_value = {"id": "0"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+        self.batch_assignment.stop()
+        self.object_assignment.stop()
+        self.makeapirequest.stop()
+        self.update_assignment.stop()
+        self.load_file.stop()
+        self.post_assignment_update.stop()
+        self.makeapirequestPatch.stop()
+        self.makeapirequestPost.stop()
+
+    def test_update_with_diffs_and_assignment(self):
+        """The count should be 1 and the post_assignment_update and makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_diffs_no_assignment(self):
+        """The count should be 1 and the makeapirequestPatch should be called."""
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 1)
+        self.assertEqual(self.makeapirequestPatch.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_with_no_diffs_and_assignment(self):
+        """The count should be 0, the post_assignment_update should be called,
+         and makeapirequestPatch should not be called."""
+
+        self.mem_data["value"][0]["testvalue"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+    def test_update_with_no_diffs_no_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPatch should not be called."""
+
+        self.mem_data["value"][0]["testvalue"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=False)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPatch.call_count, 0)
+        self.assertEqual(self.post_assignment_update.call_count, 0)
+
+    def test_update_config_not_found_and_assignment(self):
+        """The count should be 0, the post_assignment_update and makeapirequestPost should be called."""
+
+        self.mem_data["value"][0]["displayName"] = "test1"
+
+        self.count = update(self.directory.path, self.token, assignment=True)
+
+        self.assertEqual(self.count, 0)
+        self.assertEqual(self.makeapirequestPost.call_count, 1)
+        self.assertEqual(self.post_assignment_update.call_count, 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 00000000..4f8ece32
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,8 @@
+import os
+import sys
+PROJECT_PATH = os.getcwd()
+SOURCE_PATH = os.path.join(
+    PROJECT_PATH, "src"
+)
+sys.path.append(SOURCE_PATH)
+
diff --git a/tests/test_check_file.py b/tests/test_check_file.py
new file mode 100644
index 00000000..a7ef8219
--- /dev/null
+++ b/tests/test_check_file.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env/python3
+
+"""
+This module tests the check_file function.
+"""
+
+import unittest
+
+from testfixtures import TempDirectory
+from src.IntuneCD.check_file import check_file
+
+
+class TestCheckFile(unittest.TestCase):
+    """Test class for check_file."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.yaml = "file_name.yaml"
+        self.json = "file_name.json"
+        self.md = "file_name.md"
+        self.DS_Store = ".DS_Store"
+        self.d = self.directory.makedir("fake_path")
+
+        self.yaml_file = self.directory.write(
+            self.yaml, "yaml", encoding="utf-8")
+        self.json_file = self.directory.write(
+            self.json, '{"data": "fake_data"}', encoding="utf-8")
+        self.md_file = self.directory.write(self.md, "md", encoding="utf-8")
+        self.DS_Store_file = self.directory.write(
+            self.DS_Store, "DS_Store", encoding="utf-8")
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_check_yaml_file(self):
+        """Should return True if the file is a yaml file."""
+        self.check = check_file(self.directory.path, self.yaml)
+        self.assertTrue(self.check)
+
+    def test_check_json_file(self):
+        """Should return True if the file is a json file."""
+        self.check = check_file(self.directory.path, self.json)
+        self.assertTrue(self.check)
+
+    def test_check_md_file(self):
+        """Should return False if the file is a md file."""
+        self.check = check_file(self.directory.path, self.md)
+        self.assertFalse(self.check)
+
+    def test_check_DS_Store_file(self):
+        """Should return False if the file is a DS_Store file."""
+        self.check = check_file(self.directory.path, self.DS_Store)
+        self.assertFalse(self.check)
+
+    def test_check_dir(self):
+        """Should return False if the file is a directory."""
+        self.check = check_file(self.directory.path, self.d)
+        self.assertFalse(self.check)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_clean_filename.py b/tests/test_clean_filename.py
new file mode 100644
index 00000000..03c47009
--- /dev/null
+++ b/tests/test_clean_filename.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the clean_filename function.
+"""
+
+import unittest
+
+from src.IntuneCD.clean_filename import clean_filename
+
+
+class TestCleanFilename(unittest.TestCase):
+    """Test class for clean_filename."""
+
+    def test_clean_filename_string(self):
+        """The filename should be returned with the characters replaced."""
+        self.filename = "test|file:name"
+
+        result = clean_filename(self.filename)
+
+        self.assertEqual(result, "test_file_name")
+
+    def test_clean_filename_list(self):
+        """The filename should be returned with the characters replaced."""
+        self.filename = ["test|file:name", "test|file:name"]
+
+        result = clean_filename(self.filename)
+
+        self.assertEqual(result, "['test_file_name', 'test_file_name']")
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_documentation_functions.py b/tests/test_documentation_functions.py
new file mode 100644
index 00000000..7767ddc0
--- /dev/null
+++ b/tests/test_documentation_functions.py
@@ -0,0 +1,140 @@
+import unittest
+from pathlib import Path
+
+from testfixtures import TempDirectory
+from src.IntuneCD.documentation_functions import md_file, \
+    write_table, \
+    assignment_table, \
+    remove_characters, \
+    clean_list, \
+    document_configs, \
+    document_management_intents, \
+    get_md_files
+
+
+class TestDocumentationFunctions(unittest.TestCase):
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.makedir("config")
+        self.directory.makedir("intent/test")
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_md_file_does_not_exist(self):
+        """The md file should be returned."""
+        md_file(f"{self.directory.path}/test_file_name.md")
+
+        self.assertTrue(
+            Path(f"{self.directory.path}/test_file_name.md").exists())
+
+    def test_md_file_exists(self):
+        """The md file should be returned."""
+        self.directory.write("test_file_name.md", "md", encoding="utf-8")
+
+        md_file(f"{self.directory.path}/test_file_name.md")
+        with open(f"{self.directory.path}/test_file_name.md", "r") as f:
+            self.assertEqual(f.read(), '')
+
+    def test_write_table(self):
+        """The table should be returned."""
+        self.table_data = [['test', 'test'], ['test', 'test']]
+        self.expected_table = '|setting|value||-------|-----||test   |test ||test   |test |'
+
+        self.result = write_table(self.table_data)
+        self.string = str(self.result)
+
+        self.assertEqual(
+            ''.join(
+                self.string.splitlines()),
+            self.expected_table)
+
+    def test_assignment_table(self):
+        """The table should be returned."""
+        self.table_data = {
+            'assignments': [
+                {
+                    'intent': 'apply',
+                    'target': {
+                        '@odata.type': "#test",
+                        'groupName': 'test-group',
+                        'deviceAndAppManagementAssignmentFilterId': 'test-filter',
+                        'deviceAndAppManagementAssignmentFilterType': 'test'}}]}
+        self.expected_table = '|intent|  target  |filter type|filter name||------|----------|-----------|-----------||apply |test-group|test       |test-filter|'
+
+        self.result = assignment_table(self.table_data)
+        self.string = str(self.result)
+
+        self.assertEqual(''.join(
+            self.string.splitlines()), self.expected_table)
+
+    def test_remove_characters(self):
+        """The string should be returned."""
+        self.string = '@[test]{}'
+
+        self.assertEqual(remove_characters(self.string), 'test')
+
+    def test_clean_list_list(self):
+        """The list should be returned."""
+        self.list = [{'teamIdentifier': 'test', 'bundleId': 'test'}]
+        self.expected_list = ['teamIdentifier: test, bundleId: test']
+        self.result = clean_list(self.list)
+
+        self.assertEqual(self.result, self.expected_list)
+
+    def test_clean_list_dict(self):
+        """The list should be returned."""
+        self.dict = {'appName': 'test', 'type': '#microsoft.graph.iosVppApp'}
+        self.expected_list = ['appName', 'type']
+        self.result = clean_list(self.dict)
+
+        self.assertEqual(self.result, self.expected_list)
+
+    def test_clean_list_string(self):
+        """The list should be returned."""
+        self.string = ['test']
+        self.expected_list = ['test']
+        self.result = clean_list(self.string)
+
+        self.assertEqual(self.result, self.expected_list)
+
+    def test_document_configs(self):
+        """The list should be returned."""
+        self.directory.write(
+            "config/test_file_name.json",
+            '{"test": "test", "name": "test", "description": "test", "assignments": [{"intent": "apply", "target": {"@odata.type": "#test", "groupName": "test-group", "deviceAndAppManagementAssignmentFilterId": "test-filter", "deviceAndAppManagementAssignmentFilterType": "test"}}]}',
+            encoding="utf-8")
+        self.expected_data = '#test##testDescription:test###Assignments|intent|target|filtertype|filtername||------|----------|-----------|-----------||apply|test-group|test|test-filter||setting|value||-------|-----||test|test||name|test|'
+
+        document_configs(
+            f"{self.directory.path}/config",
+            f"{self.directory.path}/test.md",
+            'test',
+            10,
+            split=False)
+        with open(f"{self.directory.path}/test.md", "r") as f:
+            self.data = f.read()
+            self.result = ''.join([line.strip() for line in self.data])
+
+        self.assertEqual(self.result, self.expected_data)
+
+    def test_document_management_intents(self):
+        """The list should be returned."""
+        self.directory.write(
+            "intent/test/test_file_name.json",
+            '{"test": "test", "name": "test", "description": "test", "settingsDelta": [{"test": "test", "definitionId": "deviceConfiguration--macOSEndpointProtectionConfiguration_fileVaultNumberOfTimesUserCanIgnore","valueJson": "1","value": 1}], "assignments": [{"intent": "apply", "target": {"@odata.type": "#test", "groupName": "test-group", "deviceAndAppManagementAssignmentFilterId": "test-filter", "deviceAndAppManagementAssignmentFilterType": "test"}}]}',
+            encoding="utf-8")
+        self.expected_data = '#intent##testDescription:test###Assignments|intent|target|filtertype|filtername||------|----------|-----------|-----------||apply|test-group|test|test-filter||setting|value||-----------------------------------|-----||test|test||name|test||fileVaultNumberOfTimesUserCanIgnore|1|'
+
+        document_management_intents(
+            f"{self.directory.path}/intent/",
+            f"{self.directory.path}/test.md",
+            'intent',
+            split=False)
+        with open(f"{self.directory.path}/test.md", "r") as f:
+            self.data = f.read()
+            self.result = ''.join([line.strip() for line in self.data])
+
+        self.assertEqual(self.result, self.expected_data)
diff --git a/tests/test_get_added_removed.py b/tests/test_get_added_removed.py
new file mode 100644
index 00000000..84db821e
--- /dev/null
+++ b/tests/test_get_added_removed.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the get_added_removed function.
+"""
+
+import unittest
+
+from src.IntuneCD.update_assignment import get_added_removed
+
+
+class TestCleanFilename(unittest.TestCase):
+    """Test class for get_added_removed."""
+
+    def test_get_added_removed(self):
+        """The list of added and removed should be returned."""
+        self.object = {
+            'assignments': {
+                'intent': 'apply',
+                'target': {
+                    '@odata.type': '#microsoft.graph.allDevicesAssignmentTarget',
+                    'deviceAndAppManagementAssignmentFilterId': "1234",
+                    'deviceAndAppManagementAssignmentFilterType': 'device'}}}
+
+        result = get_added_removed(self.object)
+
+        self.assertEqual(
+            result,
+            ['intent: apply, Filter ID: 1234, Filter Type: device, target: All Devices'])
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_get_authparams.py b/tests/test_get_authparams.py
new file mode 100644
index 00000000..a36f1d4e
--- /dev/null
+++ b/tests/test_get_authparams.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the get_authparams function.
+"""
+
+import unittest
+
+from unittest.mock import patch
+from testfixtures import TempDirectory
+from src.IntuneCD.get_authparams import getAuth
+
+
+@patch('src.IntuneCD.get_authparams.getAuth')
+@patch('src.IntuneCD.get_authparams.obtain_accesstoken', return_value="token")
+class TestGetAuth(unittest.TestCase):
+    """Test class for get_auth."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.directory.write(
+            'auth_dev.json',
+            '{"params": {"DEV_CLIENT_ID": "test", "DEV_CLIENT_SECRET": "test", "DEV_TENANT_NAME": "test"}}',
+            encoding='utf-8')
+        self.directory.write(
+            'auth_prod.json',
+            '{"params": {"PROD_CLIENT_ID": "test", "PROD_CLIENT_SECRET": "test", "PROD_TENANT_NAME": "test"}}',
+            encoding='utf-8')
+        self.directory.write(
+            'auth.json',
+            '{"params": {"CLIENT_ID": "test", "CLIENT_SECRET": "test", "TENANT_NAME": "test"}}',
+            encoding='utf-8')
+        self.auth_dev_json = self.directory.path + '/auth_dev.json'
+        self.auth_prod_json = self.directory.path + '/auth_prod.json'
+        self.auth_json = self.directory.path + '/auth.json'
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_get_auth_devtoprod_env_dev(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """The auth params should be returned."""
+        with patch.dict('os.environ', {'DEV_CLIENT_ID': 'test', 'DEV_CLIENT_SECRET': 'test', 'DEV_TENANT_NAME': 'test'}):
+            result = getAuth('devtoprod', localauth=None, tenant='DEV')
+            self.assertEqual(result, "token")
+
+    def test_get_auth_devtoprod_env_prod(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """The auth params should be returned."""
+        with patch.dict('os.environ', {'PROD_CLIENT_ID': 'test', 'PROD_CLIENT_SECRET': 'test', 'PROD_TENANT_NAME': 'test'}):
+            result = getAuth('devtoprod', localauth=None, tenant='PROD')
+            self.assertEqual(result, "token")
+
+    def test_get_auth_devtoprod_localauth_dev(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """The auth params should be returned."""
+        result = getAuth(
+            'devtoprod',
+            localauth=self.auth_dev_json,
+            tenant='DEV')
+        self.assertEqual(result, "token")
+
+    def test_get_auth_devtoprod_localauth_prod(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """The auth params should be returned."""
+        result = getAuth(
+            'devtoprod',
+            localauth=self.auth_prod_json,
+            tenant='PROD')
+        self.assertEqual(result, "token")
+
+    def test_get_auth_standalone_env(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """The auth params should be returned."""
+        with patch.dict('os.environ', {'CLIENT_ID': 'test', 'CLIENT_SECRET': 'test', 'TENANT_NAME': 'test'}):
+            result = getAuth('standalone', localauth=None, tenant=None)
+            self.assertEqual(result, "token")
+
+    def test_get_auth_standalone_localauth(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """The auth params should be returned."""
+        result = getAuth('standalone', localauth=self.auth_json, tenant=None)
+        self.assertEqual(result, "token")
+
+    def test_get_auth_devtoprod_missing_env_dev(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """Exception should be raised due to missing env."""
+        with patch.dict('os.environ', {'DEV_CLIENT_ID': 'test', 'DEV_CLIENT_SECRET': 'test'}):
+            with self.assertRaises(Exception):
+                getAuth('devtoprod', localauth=None, tenant='DEV')
+
+    def test_get_auth_devtoprod_missing_env_prod(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """Exception should be raised due to missing env."""
+        with patch.dict('os.environ', {'PROD_CLIENT_ID': 'test', 'PROD_CLIENT_SECRET': 'test'}):
+            with self.assertRaises(Exception):
+                getAuth('devtoprod', localauth=None, tenant='PROD')
+
+    def test_get_auth_standalone_missing_env(
+            self, mock_getAuth, mock_obtain_accesstoken):
+        """Exception should be raised due to missing env."""
+        with patch.dict('os.environ', {'CLIENT_ID': 'test', 'CLIENT_SECRET': 'test'}):
+            with self.assertRaises(Exception):
+                getAuth('standalone', localauth=None, tenant=None)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_get_diff_output.py b/tests/test_get_diff_output.py
new file mode 100644
index 00000000..bed2919e
--- /dev/null
+++ b/tests/test_get_diff_output.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the get_diff_output function.
+"""
+
+import unittest
+
+from src.IntuneCD.get_diff_output import get_diff_output
+
+
+class TestGetDiffOutput(unittest.TestCase):
+    """Test class for get_diff_output."""
+
+    def test_get_diff_output_single(self):
+        """Should return a list with the setting, new value and old value."""
+        diff = {
+            "root['cameraBlocked']": {
+                'new_value': True,
+                'old_value': False}}
+
+        self.output = get_diff_output(diff)
+
+        self.assertEqual(
+            self.output,
+            ["Setting: 'cameraBlocked', New Value: True, Old Value: False"])
+
+    def test_get_diff_output_multiple(self):
+        """Should return a list with the settings, new values and old values."""
+        diff = {
+            "root['cameraBlocked']": {
+                'new_value': True,
+                'old_value': False},
+            "root['cameraBlocked2']": {
+                'new_value': True,
+                'old_value': False}}
+
+        self.output = get_diff_output(diff)
+
+        self.assertEqual(
+            self.output,
+            ["Setting: 'cameraBlocked', New Value: True, Old Value: False",
+             "Setting: 'cameraBlocked2', New Value: True, Old Value: False"])
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_graph_batch.py b/tests/test_graph_batch.py
new file mode 100644
index 00000000..ec842f28
--- /dev/null
+++ b/tests/test_graph_batch.py
@@ -0,0 +1,247 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the graph_batch module.
+"""
+
+import unittest
+
+from unittest.mock import patch
+from src.IntuneCD.graph_batch import batch_request, \
+    batch_assignment, \
+    batch_intents, \
+    get_object_assignment, \
+    get_object_details
+
+
+class TestGraphBatch(unittest.TestCase):
+    """Test class for graph_batch."""
+
+    def setUp(self):
+        self.token = 'token'
+        self.batch_request_data = ["1", "2", "3"]
+        self.batch_assignment_data = {'value': [{'id': '0'}]}
+        self.batch_intents_data = {
+            'value': [
+                {
+                    'id': '0',
+                    'templateId': '0',
+                    'displayName': 'test',
+                    'description': '',
+                    'roleScopeTagIds': ['0']}]}
+        self.responses = [{'value': [
+            {'target': {'groupId': '0', 'deviceAndAppManagementAssignmentFilterId': '0'}}]}]
+        self.group_responses = [{"displayName": "test", "id": "0"}]
+        self.filter_responses = [{"displayName": "test", "id": "0"}]
+        self.category_responses = [
+            {
+                '@odata.context': "https://graph.microsoft.com/beta/$metadata#deviceManagement/templates('0')/categories",
+                'value': [
+                    {
+                        'id': '0',
+                        'displayName': 'test'}]}]
+        self.settings_responses = [
+            {
+                '@odata.context':
+                    "https://graph.microsoft.com/beta/$metadata#deviceManagement/intents('0')/categories('0')/settings",
+                'value': [
+                    {
+                        '@odata.type': '#microsoft.graph.deviceManagementBooleanSettingInstance',
+                        'id': '0',
+                        'definitionId': 'Protection',
+                        'valueJson': 'null',
+                        'value': None}]}]
+
+        self.makeapirequestPost_patch = patch(
+            'src.IntuneCD.graph_batch.makeapirequestPost')
+        self.makeapirequestPost = self.makeapirequestPost_patch.start()
+
+        self.get_object_details_patch = patch(
+            'src.IntuneCD.graph_batch.get_object_details')
+        self.get_object_details = self.get_object_details_patch.start()
+
+        self.get_object_assignment_patch = patch(
+            'src.IntuneCD.graph_batch.get_object_assignment')
+        self.get_object_assignment = self.get_object_assignment_patch.start()
+
+        self.batch_intents_patch = patch(
+            'src.IntuneCD.graph_batch.batch_intents')
+        self.batch_intents = self.batch_intents_patch.start()
+
+        self.batch_assignment_patch = patch(
+            'src.IntuneCD.graph_batch.batch_assignment')
+        self.batch_assignment = self.batch_assignment_patch.start()
+
+        self.batch_request_patch = patch(
+            'src.IntuneCD.graph_batch.batch_request')
+        self.batch_request = self.batch_request_patch.start()
+        self.batch_request.side_effect = self.responses, self.group_responses, self.filter_responses
+
+    def tearDown(self):
+        self.makeapirequestPost.stop()
+        self.get_object_details.stop()
+        self.get_object_assignment.stop()
+        self.batch_intents.stop()
+        self.batch_assignment.stop()
+        self.batch_request.stop()
+
+    def test_batch_request(self):
+        """The batch request function should return the expected result."""
+
+        self.expected_result = [
+            {'odata.count': 1, 'value': [{'id': '0', 'displayName': 'test'}]}]
+        self.makeapirequestPost.return_value = {"responses": [{"id": "5", "status": 200, "body": {
+            'odata.count': 1, "value": [{"id": "0", "displayName": "test"}]}}]}
+        self.result = batch_request(
+            self.batch_request_data, 'test', 'test', self.token)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_batch_assignment(self):
+        """The batch assignment function should return the expected result."""
+
+        self.expected_result = [{'value': [{'target': {
+            'deviceAndAppManagementAssignmentFilterId': 'test', 'groupId': '0', 'groupName': 'test'}}]}]
+        self.result = batch_assignment(
+            self.batch_assignment_data, 'test', 'test', self.token)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_batch_assignment_appProtection_mdmWindowsInformationProtectionPolicy(
+            self):
+        """The batch assignment function should return the expected result for the platform."""
+
+        self.batch_assignment_data = {'value': [
+            {'id': '0', '@odata.type': '#microsoft.graph.mdmWindowsInformationProtectionPolicy'}]}
+
+        self.expected_result = [{'value': [{'target': {
+            'deviceAndAppManagementAssignmentFilterId': 'test', 'groupId': '0', 'groupName': 'test'}}]}]
+        self.result = batch_assignment(
+            self.batch_assignment_data,
+            'test',
+            'test',
+            self.token,
+            app_protection=True)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_batch_assignment_appProtection_windowsInformationProtectionPolicy(
+            self):
+        """The batch assignment function should return the expected result for the platform."""
+
+        self.batch_assignment_data = {'value': [
+            {'id': '0', '@odata.type': '#microsoft.graph.windowsInformationProtectionPolicy'}]}
+
+        self.expected_result = [{'value': [{'target': {
+            'deviceAndAppManagementAssignmentFilterId': 'test', 'groupId': '0', 'groupName': 'test'}}]}]
+        self.result = batch_assignment(
+            self.batch_assignment_data,
+            'test',
+            'test',
+            self.token,
+            app_protection=True)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_batch_assignment_appProtection_iosManagedAppProtection(self):
+        """The batch assignment function should return the expected result for the platform."""
+
+        self.batch_assignment_data = {'value': [
+            {'id': '0', '@odata.type': '#microsoft.graph.iosManagedAppProtection'}]}
+
+        self.expected_result = [{'value': [{'target': {
+            'deviceAndAppManagementAssignmentFilterId': 'test', 'groupId': '0', 'groupName': 'test'}}]}]
+        self.result = batch_assignment(
+            self.batch_assignment_data,
+            'test',
+            'test',
+            self.token,
+            app_protection=True)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_batch_intents(self):
+        """The batch intents function should return the expected result."""
+
+        self.batch_request.side_effect = self.category_responses, self.settings_responses
+        self.expected_result = {
+            'value': [
+                {
+                    'description': '',
+                    'displayName': 'test',
+                    'id': '0',
+                    'roleScopeTagIds': ['0'],
+                    'settingsDelta': [
+                        {
+                            '@odata.type': '#microsoft.graph.deviceManagementBooleanSettingInstance',
+                            'definitionId': 'Protection',
+                            'id': '0',
+                            'value': None,
+                            'valueJson': 'null'}],
+                    'templateId': '0'}]}
+        self.result = batch_intents(self.batch_intents_data, self.token)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_get_object_assignment(self):
+        """The get object assignment function should return the expected result."""
+
+        self.id = '0'
+        self.response = [
+            {
+                '@odata.context':
+                "https://graph.microsoft.com/beta/$metadata#deviceAppManagement/mobileAppConfigurations('0')/assignments",
+                'value': [
+                    {
+                        'id': '0',
+                        'target': {
+                            'groupId': '0',
+                            'groupName': 'test'}}]}]
+        self.expected_result = [{'target': {'groupName': 'test'}}]
+
+        self.result = get_object_assignment(self.id, self.response)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_get_object_details(self):
+        """The get object details function should return the expected result."""
+
+        self.id = '0'
+        self.response = [
+            {
+                '@odata.context':
+                "https://graph.microsoft.com/beta/$metadata#deviceAppManagement/mobileAppConfigurations('0')",
+                'value': [{
+                    'id': '0',
+                    'displayName': 'test',
+                    'description': '',
+                    'templateId': '0',
+                    'roleScopeTagIds': ['0'],
+                    'settingsDelta': [
+                        {
+                            '@odata.type': '#microsoft.graph.deviceManagementBooleanSettingInstance',
+                            'definitionId': 'Protection',
+                            'id': '0',
+                            'value': None,
+                            'valueJson': 'null'}]}]}]
+        self.expected_result = [{
+            'description': '',
+            'displayName': 'test',
+            'id': '0',
+            'roleScopeTagIds': ['0'],
+            'settingsDelta': [
+                {
+                    '@odata.type': '#microsoft.graph.deviceManagementBooleanSettingInstance',
+                    'definitionId': 'Protection',
+                    'id': '0',
+                    'value': None,
+                    'valueJson': 'null'}],
+            'templateId': '0'}]
+
+        self.result = get_object_details(self.id, self.response)
+
+        self.assertEqual(self.result, self.expected_result)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_graph_request.py b/tests/test_graph_request.py
new file mode 100644
index 00000000..ad88035a
--- /dev/null
+++ b/tests/test_graph_request.py
@@ -0,0 +1,348 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the graph_request module.
+"""
+
+import unittest
+
+from unittest import mock
+from unittest.mock import patch
+from src.IntuneCD.graph_request import makeapirequest, makeapirequestPost, makeapirequestPut, makeapirequestPatch
+
+
+def _mock_response(
+        self,
+        status=200,
+        content="CONTENT",
+        json_data=None,
+        raise_for_status=None):
+    """Mock the response from the requests library."""
+
+    mock_resp = mock.Mock()
+    # mock raise_for_status call w/optional error
+    mock_resp.raise_for_status = mock.Mock()
+    if raise_for_status:
+        mock_resp.raise_for_status.side_effect = raise_for_status
+    # set status code and content
+    mock_resp.status_code = status
+    mock_resp.text = content
+
+    return mock_resp
+
+
+@patch("src.IntuneCD.graph_request.makeapirequest")
+@patch("requests.get")
+@patch("time.sleep", return_value=None)
+class TestGraphRequestGet(unittest.TestCase):
+    """Test class for graph_request."""
+
+    def setUp(self):
+        self.token = {"accessToken": "token"}
+
+    def test_makeapirequest_no_q_param(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made once and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=200, content='{"value": [{"id": "0"}]}')
+        mock_get.return_value = self.mock_resp
+        self.result = makeapirequest("https://endpoint", self.token)
+        self.assertEqual(self.result, {"value": [{"id": "0"}]})
+
+    def test_makeapirequest_status_502_no_q_param(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made twice and exception should be raised."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=502, content='request timeout')
+            mock_get.return_value = self.mock_resp
+            makeapirequest("https://endpoint", self.token)
+
+        self.assertEqual(2, mock_get.call_count)
+
+    def test_makeapirequest_status_503_no_q_param(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made twice and exception should be raised."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=503, content='request timeout')
+            mock_get.return_value = self.mock_resp
+            makeapirequest("https://endpoint", self.token)
+
+        self.assertEqual(2, mock_get.call_count)
+
+    def test_makeapirequest_status_504_no_q_param(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made twice and exception should be raised."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=504, content='request timeout')
+            mock_get.return_value = self.mock_resp
+            makeapirequest("https://endpoint", self.token)
+
+        self.assertEqual(2, mock_get.call_count)
+
+    def test_makeapirequest_with_q_param(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made once and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=200, content='{"value": [{"id": "0"}]}')
+        mock_get.return_value = self.mock_resp
+        self.result = makeapirequest(
+            "https://endpoint",
+            self.token,
+            q_param="$filter=id eq '0'")
+        self.assertEqual(self.result, {"value": [{"id": "0"}]})
+
+    def test_makeapirequest_status_502_with_q_param(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made twice and exception should be raised."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=502, content='request timeout')
+            mock_get.return_value = self.mock_resp
+            makeapirequest(
+                "https://endpoint",
+                self.token,
+                q_param="$filter=id eq '0'")
+
+        self.assertEqual(2, mock_get.call_count)
+
+    def test_makeapirequest_status_503_with_q_param(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made twice and exception should be raised."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=503, content='request timeout')
+            mock_get.return_value = self.mock_resp
+            makeapirequest(
+                "https://endpoint",
+                self.token,
+                q_param="$filter=id eq '0'")
+
+        self.assertEqual(2, mock_get.call_count)
+
+    def test_makeapirequest_status_504_with_q_param(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made twice and exception should be raised."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=504, content='request timeout')
+            mock_get.return_value = self.mock_resp
+            makeapirequest(
+                "https://endpoint",
+                self.token,
+                q_param="$filter=id eq '0'")
+
+        self.assertEqual(2, mock_get.call_count)
+
+    def test_makeapirequest_odata_nextlink(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made and the response should contain next link values."""
+        self.NEXT_LINK_VALUE = {"value": [{"id": "1"}]}
+        mock_makeapirequest.return_value = self.NEXT_LINK_VALUE
+        self.mock_resp = _mock_response(
+            self,
+            status=200,
+            content='{"value": [{"id": "0"}], "@odata.nextLink": "https://endpoint"}')
+        mock_get.return_value = self.mock_resp
+        self.result = makeapirequest("https://endpoint", self.token)
+
+        self.assertEqual(self.result['value'], [{"id": "0"}, {"id": "1"}])
+
+    def test_makeapirequest_status_404(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=404, content='not found')
+        mock_get.return_value = self.mock_resp
+        makeapirequest("https://endpoint", self.token)
+
+        self.assertEqual(1, mock_get.call_count)
+
+    def test_makeapirequest_assignmentfilter_not_enabled(
+            self, mock_sleep, mock_get, mock_makeapirequest):
+        """The request should be made and assignment filters should be skipped."""
+        self.mock_resp = _mock_response(
+            self, status=500, content='{"FeatureNotEnabled": []}')
+        mock_get.return_value = self.mock_resp
+        makeapirequest("https://endpoint/assignmentFilters", self.token)
+
+        self.assertEqual(1, mock_get.call_count)
+
+    def test_makeapirequest_exception(
+            self,
+            mock_sleep,
+            mock_get,
+            mock_makeapirequest):
+        """The request should be made and exception should be raised."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=500, content='Internal Server Error')
+            mock_get.return_value = self.mock_resp
+            makeapirequest("https://endpoint/", self.token)
+
+        self.assertEqual(1, mock_get.call_count)
+
+
+@patch("src.IntuneCD.graph_request.makeapirequestPatch")
+@patch("requests.patch")
+class TestGraphRequestPatch(unittest.TestCase):
+
+    def setUp(self):
+        self.token = {"accessToken": "token"}
+
+    def test_makeapirequestPatch_no_q_param(
+            self, mock_patch, mock_makeapirequestPatch):
+        """The request should be made and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=200, content='')
+        mock_patch.return_value = self.mock_resp
+        result = makeapirequestPatch(
+            "https://endpoint",
+            self.token,
+            q_param=None,
+            jdata='{"id": "0"}')
+
+        self.assertEqual(result, None)
+
+    def test_makeapirequestPatch_with_q_param(
+            self, mock_patch, mock_makeapirequestPatch):
+        """The request should be made and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=200, content='')
+        mock_patch.return_value = self.mock_resp
+        self.result = makeapirequestPatch(
+            "https://endpoint",
+            self.token,
+            q_param="$filter=id eq '0'",
+            jdata='{"id": "0"}')
+
+        self.assertEqual(self.result, None)
+
+    def test_makeapirequestPatch_not_matching_status_code(
+            self, mock_patch, mock_makeapirequestPatch):
+        """The request should be made and the response should be returned."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=204, content='')
+            mock_patch.return_value = self.mock_resp
+            makeapirequestPatch(
+                "https://endpoint",
+                self.token,
+                q_param="$filter=id eq '0'",
+                jdata='{"id": "0"}',
+                status_code=200)
+
+        self.assertEqual(1, mock_patch.call_count)
+
+
+@patch("src.IntuneCD.graph_request.makeapirequestPost")
+@patch("requests.post")
+class TestGraphRequestPost(unittest.TestCase):
+
+    def setUp(self):
+        self.token = {"accessToken": "token"}
+        self.jdata = {"id": "0"}
+        self.content = '{"id": "0"}'
+        self.expected_result = {"id": "0"}
+
+    def test_makeapirequestPost_no_q_param(
+            self, mock_patch, mock_makeapirequestPost):
+        """The request should be made and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=200, content=self.content)
+        mock_patch.return_value = self.mock_resp
+        self.result = makeapirequestPost(
+            "https://endpoint",
+            self.token,
+            q_param=None,
+            jdata=self.jdata)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_makeapirequestPost_with_q_param(
+            self, mock_patch, mock_makeapirequestPost):
+        """The request should be made and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=200, content=self.content)
+        mock_patch.return_value = self.mock_resp
+        self.result = makeapirequestPost(
+            "https://endpoint",
+            self.token,
+            q_param="$filter=id eq '0'",
+            jdata=self.jdata)
+
+        self.assertEqual(self.result, self.expected_result)
+
+    def test_makeapirequestPost_not_matching_status_code(
+            self, mock_patch, mock_makeapirequestPost):
+        """The request should be made and the response should be returned."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=204, content='')
+            mock_patch.return_value = self.mock_resp
+            makeapirequestPost(
+                "https://endpoint",
+                self.token,
+                q_param="$filter=id eq '0'",
+                jdata=self.jdata,
+                status_code=200)
+
+        self.assertEqual(1, mock_patch.call_count)
+
+
+@patch("src.IntuneCD.graph_request.makeapirequestPut")
+@patch("requests.put")
+class TestGraphRequestPut(unittest.TestCase):
+
+    def setUp(self):
+        self.token = {"accessToken": "token"}
+
+    def test_makeapirequestPut_no_q_param(
+            self, mock_patch, mock_makeapirequestPut):
+        """The request should be made and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=200, content='')
+        mock_patch.return_value = self.mock_resp
+        self.result = makeapirequestPut(
+            "https://endpoint",
+            self.token,
+            q_param=None,
+            jdata='{"id": "0"}')
+
+        self.assertEqual(self.result, None)
+
+    def test_makeapirequestPut_with_q_param(
+            self, mock_patch, mock_makeapirequestPut):
+        """The request should be made and the response should be returned."""
+        self.mock_resp = _mock_response(
+            self, status=200, content='')
+        mock_patch.return_value = self.mock_resp
+        self.result = makeapirequestPut(
+            "https://endpoint",
+            self.token,
+            q_param="$filter=id eq '0'",
+            jdata='{"id": "0"}')
+
+        self.assertEqual(self.result, None)
+
+    def test_makeapirequestPut_not_matching_status_code(
+            self, mock_patch, mock_makeapirequestPut):
+        """The request should be made and the response should be returned."""
+        with self.assertRaises(Exception):
+            self.mock_resp = _mock_response(
+                self, status=204, content='')
+            mock_patch.return_value = self.mock_resp
+            makeapirequestPut(
+                "https://endpoint",
+                self.token,
+                q_param="$filter=id eq '0'",
+                jdata='{"id": "0"}',
+                status_code=200)
+
+        self.assertEqual(1, mock_patch.call_count)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_load_file.py b/tests/test_load_file.py
new file mode 100644
index 00000000..8a4e2ed7
--- /dev/null
+++ b/tests/test_load_file.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env/python3
+
+"""
+This module tests the load_file function.
+"""
+
+import unittest
+
+from testfixtures import TempDirectory
+from src.IntuneCD.load_file import load_file
+
+
+class TestLoadFile(unittest.TestCase):
+    """Test class for load_file."""
+
+    def setUp(self):
+        self.yaml_name = "file_name.yaml"
+        self.json_name = "file_name.json"
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.yaml_file = self.directory.write(
+            self.yaml_name,
+            "yaml",
+            encoding="utf-8")
+        self.json_file = self.directory.write(
+            self.json_name,
+            '{"data": "fake_data"}',
+            encoding="utf-8")
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_load_file(self):
+        """Yaml and json files should be returned."""
+        with open(self.yaml_file) as f:
+            self.yaml = load_file(self.yaml_name, f)
+
+        with open(self.json_file) as f:
+            self.json = load_file(self.json_name, f)
+
+        self.assertIsNotNone(self.yaml)
+        self.assertIsNotNone(self.json)
+
+    def test_load_file_invalid_format(self):
+        """Invalid file formats should raise an error."""
+        with self.assertRaises(ValueError):
+            load_file("file_name.txt", "txt")
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_match.py b/tests/test_match.py
new file mode 100644
index 00000000..58ee041e
--- /dev/null
+++ b/tests/test_match.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the match function.
+"""
+
+import unittest
+
+from src.IntuneCD.backup_applications import match
+
+
+class TestMatch(unittest.TestCase):
+    """Test class for match."""
+
+    def test_match_macOS(self):
+        """The odata type should match the expected value."""
+        self.app = {'@odata.type': '#microsoft.graph.macOsVppApp'}
+
+        self.result = ('macos', str(self.app['@odata.type']).lower())
+
+        self.assertTrue(self.result)
+
+    def test_match_ios(self):
+        """The odata type should match the expected value."""
+        self.app = {'@odata.type': '#microsoft.graph.iosStoreApp'}
+
+        self.result = ('ios', str(self.app['@odata.type']).lower())
+
+        self.assertTrue(self.result)
+
+    def test_match_windows(self):
+        """The odata type should match the expected value."""
+        self.app = {'@odata.type': '#microsoft.graph.win32LobApp'}
+
+        self.result = match('win32', str(self.app['@odata.type']).lower())
+
+        self.assertTrue(self.result)
+
+    def test_match_android(self):
+        """The odata type should match the expected value."""
+        self.app = {'@odata.type': '#microsoft.graph.androidStoreApp'}
+
+        self.result = match('android', str(self.app['@odata.type']).lower())
+
+        self.assertTrue(self.result)
+
+    def test_match_unknown(self):
+        """Unkown odata type should return false."""
+        self.app = {'@odata.type': '#microsoft.graph.unknown'}
+
+        self.result = match('iOS', str(self.app['@odata.type']).lower())
+
+        self.assertFalse(self.result)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_remove_keys.py b/tests/test_remove_keys.py
new file mode 100644
index 00000000..f39ccc30
--- /dev/null
+++ b/tests/test_remove_keys.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+
+import unittest
+
+from src.IntuneCD.remove_keys import remove_keys
+
+
+class TestRemoveKeys(unittest.TestCase):
+    """Test class for remove_keys."""
+
+    def test_remove_keys(self):
+        """The dict should be returned with the keys removed."""
+        self.dict = {
+            'id': '1',
+            'isGlobalScript': True,
+            'lastModifiedDateTime': "2022-07-06",
+            'awesome_key': 'awesome_value'}
+
+        self.dict = remove_keys(self.dict)
+
+        self.assertEqual(self.dict, {'awesome_key': 'awesome_value'})
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_save_output.py b/tests/test_save_output.py
new file mode 100644
index 00000000..00e20484
--- /dev/null
+++ b/tests/test_save_output.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the save_output function with a JSON file.
+"""
+
+import unittest
+import json
+import yaml
+
+from testfixtures import TempDirectory
+from unittest.mock import patch
+from src.IntuneCD.save_output import save_output
+
+
+@patch("src.IntuneCD.save_output")
+class TestSaveOutput(unittest.TestCase):
+    """Test class for save_output."""
+
+    def setUp(self):
+        self.directory = TempDirectory()
+        self.directory.create()
+        self.path = f"{self.directory.path}/"
+        self.fname = "file_name"
+        self.data = {"test_content": "Hello World"}
+        self.expected_data = {"test_content": "Hello World"}
+
+    def tearDown(self):
+        self.directory.cleanup()
+
+    def test_save_output_json(self, mock_save_output):
+        """The file created by the function should have the expected contents."""
+        self.save = save_output('json', self.path, self.fname, self.data)
+
+        with open(self.path + self.fname + ".json", 'r') as f:
+            self.json = json.load(f)
+
+        self.assertEqual(
+            self.json['test_content'],
+            self.expected_data['test_content'])
+
+    def test_save_output_yaml(self, mock_save_output):
+        """The file created by the function should have the expected contents."""
+        self.save = save_output('yaml', self.path, self.fname, self.data)
+
+        with open(self.path + self.fname + ".yaml", 'r') as f:
+            data = json.dumps(yaml.safe_load(f))
+            self.yaml = json.loads(data)
+
+        self.assertEqual(
+            self.yaml['test_content'],
+            self.expected_data['test_content'])
+
+    def test_save_output_invalid_format(self, mock_save_output):
+        """The function should raise an error if the format is invalid."""
+        with self.assertRaises(ValueError):
+            self.save = save_output(
+                'invalid', self.path, self.fname, self.data)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_update_frontend.py b/tests/test_update_frontend.py
new file mode 100644
index 00000000..cead84fa
--- /dev/null
+++ b/tests/test_update_frontend.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+
+"""
+This module tests the update_frontend function.
+"""
+
+import unittest
+
+from unittest import mock
+from unittest.mock import patch
+from src.IntuneCD.update_frontend import update_frontend
+
+
+def _mock_response(
+        self,
+        status=200,
+        content="CONTENT",
+        json_data=None,
+        raise_for_status=None):
+    """Mock the response on the requests module."""
+
+    mock_resp = mock.Mock()
+    # mock raise_for_status call w/optional error
+    mock_resp.raise_for_status = mock.Mock()
+    if raise_for_status:
+        mock_resp.raise_for_status.side_effect = raise_for_status
+    # set status code and content
+    mock_resp.status_code = status
+    mock_resp.text = content
+
+    return mock_resp
+
+
+@patch("src.IntuneCD.update_frontend.update_frontend")
+@patch("requests.post")
+class TestUpdateFrontend(unittest.TestCase):
+
+    def test_update_env_configured(self, mock_post, mock_update_frontend):
+        """
+        Test that the update_frontend function is called with the correct parameters.
+        """
+
+        self.mock_resp = _mock_response(
+            self, status=200, content='')
+        mock_post.return_value = self.mock_resp
+
+        with patch.dict("os.environ", {'API_KEY': 'test'}):
+            self.update = update_frontend("http://localhost:8080/update",
+                                          {"configurations": 1})
+
+            mock_post.assert_called_once_with(
+                'http://localhost:8080/update',
+                json={
+                    'configurations': 1},
+                headers={
+                    'X-API-Key': 'test'})
+
+    def test_update_env_not_configured(self, mock_post, mock_update_frontend):
+        """If the API_KEY is not configured, the function should raise an exception."""
+        with patch.dict("os.environ", {}):
+            self.assertRaises(Exception,
+                              update_frontend,
+                              "http://localhost:8080/update",
+                              {"configurations": 1})
+
+    def test_update_frontend_error(self, mock_post, mock_update_frontend):
+        """If the update failed the function should raise an exception."""
+
+        self.mock_resp = _mock_response(
+            self, status=500, content='Internal Server Error')
+        mock_post.return_value = self.mock_resp
+
+        with patch.dict("os.environ", {'API_KEY': 'test'}):
+            self.assertRaises(Exception,
+                              update_frontend,
+                              "http://localhost:8080/update",
+                              {"configurations": 1})
+
+
+if __name__ == '__main__':
+    unittest.main()