Describe the bug
Whenever filename or directory contains any of those characters: / \ : * ? < > " | the IntuneCD throws error "[WinError 267] The directory name is invalid or OSError: Invalid argument" (From error I am assuming this issue happens only on Windows, but cannot test it on other enviroment). This behavior causes the script to abort the whole module and skips to the next one. Does not perform backup of following objects in this module.
To Reproduce
Steps to reproduce the behavior: Create a policy containing one of mentioned characters and run backup on Windows.
Expected behavior
A clear and concise description of what you expected to happen.
The error is understandable as this is OS-level issue. The behavior that I would expect is not to abort the whole module when error is thrown.
Screenshots
If applicable, add screenshots to help explain your problem.
Run type (please complete the following information):
- Mode: [e.g. 0 or 1] 0
- Client [e.g. Pipeline, local machine] local machine
- Version [e.g. 1.0.2] 2.6.0
Additional context
We were able to fix it in our enviromeny using AI. Below you can find the output of the changes made:
Changes Made
Change 1: Applications.py — _save_script_win32() method
Before:
if item.get(f"{rule_type}Rules"):
for rule in item[f"{rule_type}Rules"]:
if rule.get("scriptContent"):
if self.append_id:
script_name = (
f"{item['displayName']}_{rule_type}Script__{item['id']}.ps1"
)
else:
script_name = f"{item['displayName']}_{rule_type}Script.ps1"
if not os.path.exists(script_data_path):
os.makedirs(script_data_path)
decoded = self.decode_base64(rule["scriptContent"])
f = open(
f"{script_data_path}{script_name}",
"w",
encoding="utf-8",
)
f.write(decoded)
After:
if item.get(f"{rule_type}Rules"):
for rule in item[f"{rule_type}Rules"]:
if rule.get("scriptContent"):
display_name = self._prepare_file_name(item["displayName"])
if self.append_id:
script_name = (
f"{display_name}_{rule_type}Script__{item['id']}.ps1"
)
else:
script_name = f"{display_name}_{rule_type}Script.ps1"
if not os.path.exists(script_data_path):
os.makedirs(script_data_path)
decoded = self.decode_base64(rule["scriptContent"])
with open(
f"{script_data_path}{script_name}",
"w",
encoding="utf-8",
) as f:
f.write(decoded)
What changed: Added self._prepare_file_name() to sanitize displayName. Replaced bare open()/write() with with open() to fix a file handle leak.
Change 2: Applications.py — _save_script_mac() method
Before:
if item.get(f"{script_type}InstallScript"):
if item[f"{script_type}InstallScript"].get("scriptContent"):
if self.append_id:
script_name = f"{item['displayName']}_{script_type}InstallScript__{item['id']}.sh"
else:
script_name = f"{item['displayName']}_{script_type}InstallScript.sh"
...
f = open(
f"{script_data_path}{script_name}",
"w",
encoding="utf-8",
)
f.write(decoded)
After:
if item.get(f"{script_type}InstallScript"):
if item[f"{script_type}InstallScript"].get("scriptContent"):
display_name = self._prepare_file_name(item["displayName"])
if self.append_id:
script_name = f"{display_name}_{script_type}InstallScript__{item['id']}.sh"
else:
script_name = f"{display_name}_{script_type}InstallScript.sh"
...
with open(
f"{script_data_path}{script_name}",
"w",
encoding="utf-8",
) as f:
f.write(decoded)
What changed: Same as above — sanitization added, file handle leak fixed.
Change 3: ComplianceScripts.py — _save_script() method
Before:
if item.get("detectionScriptContent"):
if self.append_id:
script_name = (
f"{item['displayName'].replace('.ps1', '')}__{item['id']}.ps1"
)
else:
script_name = f"{item['displayName'].replace('.ps1', '')}.ps1"
...
f = open(
f"{self.script_data_path}{script_name}",
"w",
encoding="utf-8",
)
f.write(decoded)
After:
if item.get("detectionScriptContent"):
script_name_base = self._prepare_file_name(item['displayName'].replace('.ps1', ''))
if self.append_id:
script_name = f"{script_name_base}__{item['id']}.ps1"
else:
script_name = f"{script_name_base}.ps1"
...
with open(
f"{self.script_data_path}{script_name}",
"w",
encoding="utf-8",
) as f:
f.write(decoded)
What changed: Added _prepare_file_name() sanitization. Replaced bare open() with with open().
Change 4: ComplianceScripts.py — main() method, script saving loop
Before:
for item in script_data_responses:
self._save_script(item)
After:
for item in script_data_responses:
try:
self._save_script(item)
except Exception as e:
self.log(tag="error", msg=f"Error saving script for {item.get('displayName', 'unknown')}: {e}")
What changed: Added error isolation so one failed script save doesn't abort the loop.
Change 5: DeviceConfigurations.py — mobileconfig file saving
Before:
f = open(
self.path + "mobileconfig/" + item["payloadFileName"],
"w",
encoding="utf-8",
)
After:
f = open(
self.path + "mobileconfig/" + self._prepare_file_name(item["payloadFileName"]),
"w",
encoding="utf-8",
)
What changed: Added _prepare_file_name() to sanitize payloadFileName from the API response.
Change 6: ReusableSettings.py — _save_script() method
Before:
if self.append_id:
script_name = f"{item['displayName'].replace('.sh', '')}__{item['id']}.sh"
else:
script_name = f"{item['displayName'].replace('.sh', '')}.sh"
After:
if self.append_id:
script_name = f"{self._prepare_file_name(item['displayName'].replace('.sh', ''))}__{item['id']}.sh"
else:
script_name = f"{self._prepare_file_name(item['displayName'].replace('.sh', ''))}.sh"
What changed: Added _prepare_file_name() sanitization.
Change 7: ManagementIntents.py — template name used as directory path
Before:
self.path = f"{self.path}{template_type}/"
After:
self.path = f"{self.path}{self._prepare_file_name(template_type)}/"
What changed: Added _prepare_file_name() to sanitize the template display name used as a subdirectory. This was the direct cause of the [WinError 267] error with "Preview: MDM Security Baseline...".
Change 8: BaseBackupModule.py — _process_multiple_items() loop
Before:
results = {"config_count": 0, "outputs": []}
for item in data:
item_results = self._process_single_item(
item,
filetype,
path,
name_key,
log_message,
audit_compare_info,
assignment_responses,
)
results["config_count"] += item_results["config_count"]
results["outputs"].extend(item_results["outputs"])
return results
After:
results = {"config_count": 0, "outputs": []}
for item in data:
try:
item_results = self._process_single_item(
item,
filetype,
path,
name_key,
log_message,
audit_compare_info,
assignment_responses,
)
results["config_count"] += item_results["config_count"]
results["outputs"].extend(item_results["outputs"])
except Exception as e:
item_name = item.get(name_key, item.get("id", "unknown"))
self.log(
tag="error",
msg=f"Error processing item '{item_name}': {e}",
)
return results
What changed: Wrapped the loop body in try/except. A single item failure is now logged as an error but does not prevent the remaining items in the module from being backed up.
Files Modified
| File |
Changes |
IntuneCD/backup/Intune/Applications.py |
Sanitization in _save_script_win32() and _save_script_mac(), file handle leak fix |
IntuneCD/backup/Intune/ComplianceScripts.py |
Sanitization in _save_script(), error isolation in script loop |
IntuneCD/backup/Intune/DeviceConfigurations.py |
Sanitization of payloadFileName |
IntuneCD/backup/Intune/ReusableSettings.py |
Sanitization in _save_script() |
IntuneCD/backup/Intune/ManagementIntents.py |
Sanitization of template name used as directory |
IntuneCD/intunecdlib/BaseBackupModule.py |
Error isolation in _process_multiple_items() loop |
Describe the bug
Whenever filename or directory contains any of those characters:
/ \ : * ? < > " |the IntuneCD throws error "[WinError 267] The directory name is invalidorOSError: Invalid argument" (From error I am assuming this issue happens only on Windows, but cannot test it on other enviroment). This behavior causes the script to abort the whole module and skips to the next one. Does not perform backup of following objects in this module.To Reproduce
Steps to reproduce the behavior: Create a policy containing one of mentioned characters and run backup on Windows.
Expected behavior
A clear and concise description of what you expected to happen.
The error is understandable as this is OS-level issue. The behavior that I would expect is not to abort the whole module when error is thrown.
Screenshots
If applicable, add screenshots to help explain your problem.
Run type (please complete the following information):
Additional context
We were able to fix it in our enviromeny using AI. Below you can find the output of the changes made:
Changes Made
Change 1:
Applications.py—_save_script_win32()methodBefore:
After:
What changed: Added
self._prepare_file_name()to sanitizedisplayName. Replaced bareopen()/write()withwith open()to fix a file handle leak.Change 2:
Applications.py—_save_script_mac()methodBefore:
After:
What changed: Same as above — sanitization added, file handle leak fixed.
Change 3:
ComplianceScripts.py—_save_script()methodBefore:
After:
What changed: Added
_prepare_file_name()sanitization. Replaced bareopen()withwith open().Change 4:
ComplianceScripts.py—main()method, script saving loopBefore:
After:
What changed: Added error isolation so one failed script save doesn't abort the loop.
Change 5:
DeviceConfigurations.py— mobileconfig file savingBefore:
After:
What changed: Added
_prepare_file_name()to sanitizepayloadFileNamefrom the API response.Change 6:
ReusableSettings.py—_save_script()methodBefore:
After:
What changed: Added
_prepare_file_name()sanitization.Change 7:
ManagementIntents.py— template name used as directory pathBefore:
After:
What changed: Added
_prepare_file_name()to sanitize the template display name used as a subdirectory. This was the direct cause of the[WinError 267]error with"Preview: MDM Security Baseline...".Change 8:
BaseBackupModule.py—_process_multiple_items()loopBefore:
After:
What changed: Wrapped the loop body in try/except. A single item failure is now logged as an error but does not prevent the remaining items in the module from being backed up.
Files Modified
IntuneCD/backup/Intune/Applications.py_save_script_win32()and_save_script_mac(), file handle leak fixIntuneCD/backup/Intune/ComplianceScripts.py_save_script(), error isolation in script loopIntuneCD/backup/Intune/DeviceConfigurations.pypayloadFileNameIntuneCD/backup/Intune/ReusableSettings.py_save_script()IntuneCD/backup/Intune/ManagementIntents.pyIntuneCD/intunecdlib/BaseBackupModule.py_process_multiple_items()loop