{"id":14980345,"url":"https://github.com/codaamok/get-cmunusedsources","last_synced_at":"2026-02-13T07:11:42.038Z","repository":{"id":79228305,"uuid":"170016473","full_name":"codaamok/Get-CMUnusedSources","owner":"codaamok","description":"A PowerShell script that will tell you what folders are not used by Microsoft Endpoint Manager Configuration Manager.","archived":false,"fork":false,"pushed_at":"2020-03-03T11:20:15.000Z","size":331,"stargazers_count":35,"open_issues_count":3,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-02-05T21:06:31.731Z","etag":null,"topics":["configmgr","mecm","memcm","powershell","powershell-script","sccm","sccm-administration","sccm-environment","sccm-server"],"latest_commit_sha":null,"homepage":"","language":"PowerShell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codaamok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-10T19:20:26.000Z","updated_at":"2024-06-08T22:56:45.000Z","dependencies_parsed_at":"2023-06-01T18:30:36.685Z","dependency_job_id":null,"html_url":"https://github.com/codaamok/Get-CMUnusedSources","commit_stats":{"total_commits":166,"total_committers":3,"mean_commits":"55.333333333333336","dds":0.04216867469879515,"last_synced_commit":"d95837506ab790dee7813d381f211f9aa420c570"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codaamok%2FGet-CMUnusedSources","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codaamok%2FGet-CMUnusedSources/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codaamok%2FGet-CMUnusedSources/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codaamok%2FGet-CMUnusedSources/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codaamok","download_url":"https://codeload.github.com/codaamok/Get-CMUnusedSources/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238743847,"owners_count":19523188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configmgr","mecm","memcm","powershell","powershell-script","sccm","sccm-administration","sccm-environment","sccm-server"],"created_at":"2024-09-24T14:01:38.274Z","updated_at":"2026-02-13T07:11:41.972Z","avatar_url":"https://github.com/codaamok.png","language":"PowerShell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Get-CMUnusedSources\n\n## Table of contents\n\n1. [Description](#description)\n2. [Requirements](#requirements)\n3. [Getting started](#getting-started)\n4. [Default conditions](#default-conditions)\n5. [What can it do](#what-can-it-do)\n6. [Examples](#examples)\n7. [Runtime stats](#runtime-stats)\n8. [Process overview](#process-overview)\n9. [Validating the results](#validating-the-results)\n10. [The Excel report explained](#the-excel-report-explained)\n11. [The log file explained](#the-log-file-explained)\n12. [Parameters](#parameters)\n13. [Author](#author)\n14. [License](#license)\n15. [Acknowledgements](#acknowledgements)\n\n## Description\n\nA PowerShell script that will tell you what folders are not used by System Center Configuration Manager in a given path. This is useful if your storage is getting full and you need a way to identify what on disk is good to go.\n\nWatch [this video](https://www.youtube.com/watch?v=YGwQIUhYJsY) of me demoing it at WMUG.\n\nThe script returns an array of PSObjects with two properties: `Folder` and `UsedBy`.\n\nThe `UsedBy` property can have one or more of the following values:\n\n- A list of names for the content objects used by the folder.*\n- `An intermediate folder (sub or parent folder)`: a sub or parent folders of a folder used by a content object.\n- `Access denied` or `Access denied (elevation required`): a folder that the user running the script does not have read access to.\n- `Not used`: a folder that is not used by any content objects.\n\n\\* for Applications, the naming convention will be `\u003cApplication name\u003e::\u003cDeployment Type name\u003e`.\n\nFor example:\n\n```powershell\nPS C:\\\u003e $result | Select -First 10\n\nFolder                                    UsedBy\n------                                    ------\n\\\\fileserver\\Applications$                An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\.NET3.5        Not used\n\\\\fileserver\\Applications$\\.NET3.5SP1     Not used\n\\\\fileserver\\Applications$\\7zip           An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\7zip\\uninstall 7-Zip 19.00::7-Zip 19.00 - Windows Installer (*.msi file)\n\\\\fileserver\\Applications$\\7zip\\x64       7-Zip 19.00::7-Zip 19.00 (x64 edition) - Windows Installer (*.msi file)\n\\\\fileserver\\Applications$\\7zip\\x86       7-Zip 19.00::7-Zip 19.00 - Windows Installer (*.msi file)\n\\\\fileserver\\Applications$\\chrome         An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\chrome\\x64     Chrome 73.0.3683.103::Google Chrome x64\n\\\\fileserver\\Applications$\\chrome\\x86     Chrome 73.0.3683.103::Google Chrome x86\n```\n\n\u003e **Note:** I refer to a \"content object\" as an object within ConfigMgr that has a source path associated with it i.e. an object with content. This includes:\n\u003e - Packages\n\u003e - Applications\n\u003e - Drivers\n\u003e - Driver packages\n\u003e - Operating System images\n\u003e - Operating System upgrade images\n\u003e - Boot images\n\u003e - Deployment packages\n\n## Requirements\n\n- ConfigMgr console installed\n- PowerShell 5.1 or newer\n- [ImportExcel](https://github.com/dfinke/ImportExcel) module installed (only if you specify `-ExcelReport` switch). See an example of the Excel report [here](https://www.cookadam.co.uk/Get-CMUnusedSources.ps1.xlsx).\n\n## Getting started\n\n1. Download `Get-CMUnusedSources.ps1`\n2. Check out the examples below and read through the parameters available. If you're eager, then calling the script as is and only providing values for the mandatory parameters will get you going under the default conditions (_see below_).\n\n## Default conditions\n\n```powershell\nPS C:\\\u003e $result = .\\Get-CMUnusedSources.ps1 -SourcesLocation \"\\\\server\\folder\" -SiteServer \"server.contoso.com\"\n```\n\nRunning the script without anything other than the mandatory parameters will do the following:\n\n- Gather all content objects under the site code of site server given in -SiteServer\n- Show overall progress using Write-Progress\n- No logging\n- No PowerShell object export\n- No Excel report\n- Number of threads will be number from environment variable `NUMBER_OF_PROCESSORS`\n\n## What can it do\n\n- You can execute this script remotely from a site server. It makes zero changes to your ConfigMgr site. It's purely for reporting.\n- The returned object by the script is an array of PSObjects.\n- `-SourcesLocation` can be a UNC or local path and works around the 260 MAX_PATH limit. Use `-ExcludeFolders` parameter which takes an array of absolute paths of folders you wish to exclude under `-SourcesLocation`.\n- `-Threads` to control how many threads are used for concurrent processing.\n- Optionally exports PowerShell objects to file of either all your ConfigMgr content objects or the result. You can later reimport these using `Import-Clixml`.\n- Optionally create an Excel report.\n- Optionally filter the ConfigMgr content object search by specifying one or more of the following:  `-Applications`,  `-Packages`,  `-Drivers`,  `-DriverPackages`,  `-OSImages`,  `-OSUpgradeImages`,  `-BootImages`,  `-DeploymentPackages`.\n- Optionally create a log file.\n- Optionally produce an Excel report, with thanks to [ImportExcel](https://github.com/dfinke/ImportExcel). See an example of the Excel report [here](https://www.cookadam.co.uk/Get-CMUnusedSources.ps1.xlsx).\n\n## Examples\n\n```powershell\nPS C:\\\u003e $result = .\\Get-CMUnusedSources.ps1 -SourcesLocation \"\\\\server\\folder\" -SiteServer \"server.contoso.com\" -Log -ExportReturnObject -ExcelReport -ExcludeFolders \"\\\\server\\folder\\somechildfolder1\", \"\\\\server\\folder\\somechildfolder2\" -Threads 2\n```\n\n- Gather all content objects relevant to site code `XYZ`.\n- Gather all folders under `\\\\server\\folder`.\n- Exclude gathering (and therefore later processing) folders under `\\\\server\\folder\\somechildfolder1` and `\\\\server\\folder\\somechildfolder2`.\n- A log file will be created in the same directory as the script and rolled over when it reaches 2MB, with no limit on number of rotated logs to keep.\n- When finished, the object returned by the script will be exported and also the Excel report too.\n- 2 threads will be used.\n- Returns the result PowerShell object to variable `$result`.\n\n---\n\n```powershell\nPS C:\\\u003e $result = .\\Get-CMUnusedSources.ps1 -SourcesLocation \"F:\\some\\folder\" -SiteServer \"server.contoso.com\" -Log -NoProgress -ExportReturnObject -ExportCMContentObjects -Packages -Applications -OSImages -OSUpgradeImages -ExcelReport\n```\n\n- Gather all content objects relevant to site code `XYZ`.\n- Gather all folders under `F:\\some\\folder`.\n- A log file will be created in the same directory as the script and rolled over when it reaches 2MB, with no limit on number of rotated logs to keep.\n- Suppress PowerShell progress (`Write-Progress`).\n- Exports the result PowerShell object to file saved in the same directory as the script.\n- Exports all searched ConfigMgr content objects to file saved in the same directory as the script.\n- Gathers only Packages, Applications, Operating System images and Operating System upgrade images content objects.\n- Produces an Excel report saved in the same directory as the script. See an example of the Excel report [here](https://www.cookadam.co.uk/Get-CMUnusedSources.ps1.xlsx).\n- Will use as many threads as the value in environment variable `NUMBER_OF_PROCESSORS` because that's the default value of `-Threads`.\n- Returns the result PowerShell object to variable `$result`\n\n## Runtime stats\n\nThe below stats are an average of 3 runs using the following options:\n\n```powershell\nPS C:\\\u003e $result = .\\Get-CMUnusedSources.ps1 -SourcesLocation \"\\\\server\\folder\" -SiteServer \"server.contoso.com\"\n```\n\n**Folders:** 2633 - **Content objects:** 132 - **CPUs:** 2 - **RAM:** 8GB - **Runtime:** 4 minutes 20 seconds\n\n... more come\n\n## Process overview\n\nThe process begins by gathering all folders under `-SourcesLocation`. Once all folder are gathered, all (or selective) ConfigMgr content objects are gathered within a hierarchy, filtered by site code, using the ConfigMgr cmdlets.\n\n\u003e **Note:** It's OK if you see \"Access denied\" exceptions printed to console, the script will handle these and report accordingly.\n\nThe source path for each content object is manipulated to create every possible valid permutation. For example, say package P01000CD has a source path `\\\\server\\Applications$\\7zip\\x64`. The script will create an AllPaths property with a list like this:\n\n```text\n\\\\192.168.175.11\\Applications$\\7zip\\x64\n\\\\192.168.175.11\\F$\\Applications\\7zip\\x64\n\\\\192.168.175.11\\SomeOtherSharedFolder$\\7zip\\x64\n\\\\server.contoso.com\\Applications$\\7zip\\x64\n\\\\server.contoso.com\\F$\\Applications\\7zip\\x64\n\\\\server.contoso.com\\SomeOtherSharedFolder$\\7zip\\x64\n\\\\server\\Applications$\\7zip\\x64\n\\\\server\\F$\\Applications\\7zip\\x64\n\\\\server\\SomeOtherSharedFolder$\\7zip\\x64\nF:\\Applications\\7zip\\x64\n````\n\nIn the above example, the script discovered the local path for the `Applications$` share was `F:\\Applications` and that `SomeOtherSharedFolder$` was another share that also resolves to the same local path. This path permutation enables the script to identify used folders that could use different paths but resolve to the same location.\n\nOnce all the folders and ConfigMgr content objects have been gathered, it begins iterating through through each folder, and for each folder it iterates over all content objects to determine if said folder is used by any content objects. \n\nThis process builds an array of PSObjects with the two properties `Folder` and `UsedBy`.\n\nThe `UsedBy` property can have one or more of the following values:\n\n- A list of names for the content objects used by the folder.*\n- `An intermediate folder (sub or parent folder)`: a sub or parent folders of a folder used by a content object.\n- `Access denied` or `Access denied (elevation required)`: a folder that the user running the script does not have read access to.\n- `Not used`: a folder that is not used by any content objects.\n\n\\* for Applications, the naming convention will be `\u003cApplication name\u003e::\u003cDeployment Type name\u003e`.\n\n### Example output\n\n```powershell\nPS C:\\\u003e $result = .\\Get-CMUnusedSources.ps1 -SourcesLocation \"\\\\fileserver\\Applications$\" -SiteServer \"server.contoso.com\" -Applications\n[ 00:18:14 | 00:00:00 ] - Starting\n[ 00:18:14 | 00:00:00 ] - Gathering folders: \\\\fileserver\\Applications$\n[ 00:18:14 | 00:00:00 ]   - Done, number of gathered folders: 216\n[ 00:18:14 | 00:00:00 ] - Gathering content objects: Application\n[ 00:18:19 | 00:00:05 ]   - Done, number of gathered content objects: 74\n[ 00:18:19 | 00:00:05 ] - Determining unused folders, using 4 threads\n[ 00:18:19 | 00:00:05 ]   - Adding jobs to queue\n[ 00:18:23 | 00:00:09 ]       - Done, waiting for jobs to complete\n[ 00:18:31 | 00:00:17 ]   - Done determining unused folders\n[ 00:18:32 | 00:00:17 ] - Calculating used disk space by unused folders\n[ 00:18:32 | 00:00:17 ]   - Done calculating used disk space by unused folders\n[ 00:18:32 | 00:00:17 ] - ---------------------------------------------------------------------------\n[ 00:18:32 | 00:00:17 ] - Folders in \\\\fileserver\\Applications$: 216\n[ 00:18:32 | 00:00:17 ] - Folders where access denied: 2\n[ 00:18:32 | 00:00:17 ] - Folders unused: 64\n[ 00:18:32 | 00:00:17 ] - Potential disk space savings in \"\\\\fileserver\\Applications$\": 1661.43 MB\n[ 00:18:32 | 00:00:17 ] - Content objects processed: Application\n[ 00:18:32 | 00:00:17 ] - Content objects: 74\n[ 00:18:32 | 00:00:17 ] - Runtime: 00:00:17.7590537\n[ 00:18:32 | 00:00:17 ] - ---------------------------------------------------------------------------\n[ 00:18:32 | 00:00:17 ] - Finished\n\nPS C:\\\u003e $result | Select -First 10\n\nFolder                                    UsedBy\n------                                    ------\n\\\\fileserver\\Applications$                An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\.NET3.5        Not used\n\\\\fileserver\\Applications$\\.NET3.5SP1     Not used\n\\\\fileserver\\Applications$\\7zip           An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\7zip\\uninstall 7-Zip 19.00::7-Zip 19.00 - Windows Installer (*.msi file)\n\\\\fileserver\\Applications$\\7zip\\x64       7-Zip 19.00::7-Zip 19.00 (x64 edition) - Windows Installer (*.msi file)\n\\\\fileserver\\Applications$\\7zip\\x86       7-Zip 19.00::7-Zip 19.00 - Windows Installer (*.msi file)\n\\\\fileserver\\Applications$\\chrome         An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\chrome\\x64     Chrome 73.0.3683.103::Google Chrome x64\n\\\\fileserver\\Applications$\\chrome\\x86     Chrome 73.0.3683.103::Google Chrome x86\n```\n\n## Validating the results\n\nYou could verify if a folder structure is used or not by checking out the results of the script. The Excel report has a tab \"Content objects\" where you can verify if part or all of the path you're curious about is used.\n\nYou can easily achieve this verification with PowerShell:\n\n```powershell\nPS C:\\\u003e $result = .\\Get-CMUnusedSources.ps1 -SourcesLocation \"\\\\fileserver\\Applications$\" -SiteServer \"server.contoso.com\" -Applications\n[ 00:18:14 | 00:00:00 ] - Starting\n[ 00:18:14 | 00:00:00 ] - Gathering folders: \\\\fileserver\\Applications$\n[ 00:18:14 | 00:00:00 ]   - Done, number of gathered folders: 216\n[ 00:18:14 | 00:00:00 ] - Gathering content objects: Application\n[ 00:18:19 | 00:00:05 ]   - Done, number of gathered content objects: 74\n[ 00:18:19 | 00:00:05 ] - Determining unused folders, using 4 threads\n[ 00:18:19 | 00:00:05 ]   - Adding jobs to queue\n[ 00:18:23 | 00:00:09 ]       - Done, waiting for jobs to complete\n[ 00:18:31 | 00:00:17 ]   - Done determining unused folders\n[ 00:18:32 | 00:00:17 ] - Calculating used disk space by unused folders\n[ 00:18:32 | 00:00:17 ]   - Done calculating used disk space by unused folders\n[ 00:18:32 | 00:00:17 ] - ---------------------------------------------------------------------------\n[ 00:18:32 | 00:00:17 ] - Folders in \\\\fileserver\\Applications$: 216\n[ 00:18:32 | 00:00:17 ] - Folders where access denied: 2\n[ 00:18:32 | 00:00:17 ] - Folders unused: 64\n[ 00:18:32 | 00:00:17 ] - Potential disk space savings in \"\\\\fileserver\\Applications$\": 1661.43 MB\n[ 00:18:32 | 00:00:17 ] - Content objects processed: Application\n[ 00:18:32 | 00:00:17 ] - Content objects: 74\n[ 00:18:32 | 00:00:17 ] - Runtime: 00:00:17.7590537\n[ 00:18:32 | 00:00:17 ] - ---------------------------------------------------------------------------\n[ 00:18:32 | 00:00:17 ] - Finished\n\nPS C:\\\u003e $result | Where-Object { $_.Folder -like \"\\\\fileserver\\Applications*\" }\n\nFolder                                UsedBy\n------                                ------\n\\\\fileserver\\Applications$\\.NET3.5    Not used\n\\\\fileserver\\Applications$\\.NET3.5SP1 Not used\n\\\\fileserver\\Applications$\\Office     Not used\n\\\\fileserver\\Applications$\\Office\\x64 Not used\n\\\\fileserver\\Applications$\\Office\\x86 Not used\n...\n```\n\nThe `-ExportCMContentObjects` is also a useful switch for this task because it produces an XML export of the result produced by `Get-CMContent`. `Get-CMContent` is a function within the script that retrieves content objects using the Configuration Manager PowerShell cmdlets and selecting only key properties.\n\nFor all the content objects `Get-CMContent` gathers, the following properties are selected:\n\n- `ContentType`\n- `UniqueID`\n- `Name`\n- `SourcePath`\n- `SourcePathFlag`\n- `AllPaths`\n- `SizeMB`\n\nThe `SourcePathFlag` property is an enum which can have the following fours values:\n\n- `0` = `[FileSystemAccessState]::ERROR_SUCCESS`\n- `3` = `[FileSystemAccessState]::ERROR_PATH_NOT_FOUND`\n- `5` = `[FileSystemAccessState]::ERROR_ACCESS_DENIED`\n- `740` = `[FileSystemAccessState]::ERROR_ELEVATION_REQUIRED`\n\nBelow is an example of how to generate the result into XML and import it in to a different variable. From there you can do any filter you wish using `.Where()` or `Where-Object { .. }`.\n\n```powershell\nPS C:\\\u003e $result = .\\Get-CMUnusedSources.ps1 -SourcesLocation \"\\\\fileserver\\Applications$\" -SiteServer \"server.contoso.com\" -Applications -ExportCMContentObjects\n...\nPS C:\\\u003e $cmcontentobjs = Import-Clixml -Path \".\\Get-CMUnusedSources.ps1_2019-07-07_16-48-52_cmobjects.xml\"\nPS C:\\\u003e $cmcontentobjs | Select -First 2\n\nContentType    : Application\nUniqueID       : DeploymentType_0ab33a06-96ee-441a-83d8-3d3b6d0be224\nName           : Chrome 73.0.3683.103::Google Chrome x86\nSourcePath     : \\\\fileserver.contoso.com\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\\\nSourcePathFlag : 0\nAllPaths       : {\\\\192.168.175.11\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86,\n                 \\\\fileserver\\Applications1992\\chrome\\chrome 73.0.3683.103\\Google Chrome x86,\n                 \\\\fileserver.contoso.com\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86,\n                 \\\\fileserver.contoso.com\\F$\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x86...}\nSizeMB         : 56.48\n\n\nContentType    : Application\nUniqueID       : DeploymentType_f77958c3-2d93-4ac7-a81d-02e83aa69b83\nName           : Chrome 73.0.3683.103::Google Chrome x64\nSourcePath     : \\\\fileserver.contoso.com\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\\\nSourcePathFlag : 0\nAllPaths       : {\\\\fileserver\\F$\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x64,\n                 \\\\192.168.175.11\\Sources$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64,\n                 \\\\fileserver.contoso.com\\Sources$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64,\n                 \\\\fileserver.contoso.com\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64...}\nSizeMB         : 57.36\n```\n\nThe `AllPaths` property is a hashtable.\n\n```powershell\nPS C:\\\u003e $cmcontentobjs | Select-Object -ExpandProperty AllPaths -First 2 | Select-Object -ExpandProperty Keys\n\n\\\\192.168.175.11\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver\\Applications1992\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver.contoso.com\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver.contoso.com\\F$\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver.contoso.com\\Applications1992\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\192.168.175.11\\Applications1992\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver\\F$\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\192.168.175.11\\Sources$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver.contoso.com\\Sources$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver\\Sources$\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\192.168.175.11\\F$\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\nF:\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x86\n\\\\fileserver\\F$\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\192.168.175.11\\Sources$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\fileserver.contoso.com\\Sources$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\fileserver.contoso.com\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\fileserver\\Applications1992\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\192.168.175.11\\F$\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\nF:\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\fileserver\\Sources$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\fileserver\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\fileserver.contoso.com\\Applications1992\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\192.168.175.11\\Applications1992\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\fileserver.contoso.com\\F$\\Applications\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n\\\\192.168.175.11\\Applications$\\chrome\\chrome 73.0.3683.103\\Google Chrome x64\n```\n\n## The Excel report explained\n\nSee an example of the Excel report [here](https://www.cookadam.co.uk/Get-CMUnusedSources.ps1.xlsx).\n\nYou'll see five tabs: `Result`, `Summary`, `Not used folders`, `Content objects` and `Invalid paths`.\n\n### Result\n\nAll of the folders under `-SourcesLocation` and their UsedBy status (same as the PSObject returned by the script).\n\n### Summary\n\nA list of folders that were determined not used under the given path by the searched content objects. It does not include child folders, only \"unique root folders\", so this produces an accurate measurement of capacity used.\n\nFor example, if `\\\\server\\share\\7zip`, `\\\\server\\share\\7zip\\x64` and `\\\\server\\share\\7zip\\x86` were all marked as \"Not used\" then this view will only list `\\\\server\\share\\7zip`. \n\nThis ensures the \"Totals\" column produces an accurate measure of total capacity, file count or directory account for all folders marked as \"Not used\".\n\n### All not used folders\n\nAll folders that were determined not used under the given path by the searched content objects.\n\n### Invalid paths\n\nContent objects that have a source path which are not accessible from the computer that ran this script.\n\nA common result you'll see here, if you run the script remote from a site server, is things like USMT packages that are traditionally stored as a Package with a local path rather than UNC path.\n\n### Content objects\n\nAll searched ConfigMgr content objects. For example, if you specified `-Drivers` and `-DriverPackages` then it would only show you Driver and DriverPackage content object types, because that's all that was gathered.\n\nFor each content object, you'll see these properties:\n\n- `ContentType`\n- `UniqueID`\n- `Name`\n- `SourcePath`\n- `SourcePathFlag`\n- `AllPaths`\n- `SizeMB`\n\n## The log file explained\n\nYou'll need CMTrace to read it.\n\nThe log will display everything you see printed to console as well as gathered content object properties and the result.\n\nAfter gathering each content object, it will log all of those content objects and their properties like so: \n\n\u003e ContentType - UniqueId - Name - SourcePath - SourcePathFlag - AllPaths - SizeMB\n\nExample: \n\n\u003e Application - DeploymentType_1cd4198d-bb1f-41a8-ad3e-a81f4d8f1d44 - PuTTY 0.71::PuTTY x64 - \\\\\\\\fileserver.contoso.com\\\\Applications$\\\\putty\\\\putty 0.71\\\\PuTTY x64\\\\ - 0 - \\\\\\\\fileserver\\\\Applications$\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\fileserver.contoso.com\\\\F$\\\\Applications\\\\putty\\\\putty 0.71\\\\PuTTY x64,F:\\\\Applications\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\fileserver.contoso.com\\\\Applications$\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\fileserver\\\\Applications1992\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\fileserver.contoso.com\\\\Sources$\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\192.168.175.11\\\\Sources$\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\fileserver\\\\Sources$\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\fileserver\\\\F$\\\\Applications\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\192.168.175.11\\\\Applications$\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\192.168.175.11\\\\Applications1992\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\192.168.175.11\\\\F$\\\\Applications\\\\putty\\\\putty 0.71\\\\PuTTY x64,\\\\\\\\fileserver.contoso.com\\\\Applications1992\\\\putty\\\\putty 0.71\\\\PuTTY x64 - 0.73\n\nThe result of the script is also written to log like so:\n\n\u003e Folder: UsedBy\n\nExample:\n\n```\n...\n\\\\fileserver\\Applications$\\python3: An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\python3\\python3 3.7.3: An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\python3\\python3 3.7.3\\Python x64: Python 3.7.3::Python x64\n\\\\fileserver\\Applications$\\python3\\python3 3.7.3\\Python x86: Python 3.7.3::Python x86\n\\\\fileserver\\Applications$\\treesizefree: An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\treesizefree\\treesizefree 4.3.1: An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\treesizefree\\treesizefree 4.3.1\\TreeSize Free: TreeSize Free 4.3.1::TreeSize Free\n\\\\fileserver\\Applications$\\vlc: An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\vlc\\vlc 3.06: An intermediate folder (sub or parent folder)\n\\\\fileserver\\Applications$\\vlc\\vlc 3.06\\VLC x64: VLC Media Player 3.06::VLC x64\n\\\\fileserver\\Applications$\\vlc\\vlc 3.06\\VLC x86: VLC Media Player 3.06::VLC x86\n...\n```\n\nYou may spot some highlighted yellow warnings or red errors so I'll explain some of them here.\n\n\u003e Unable to interpret path \"x\"\n\nOccurs during the gathering content objects stage and trying to build the AllPaths property.\n\nOne of your content objects in ConfigMgr has a funky source path that I couldn't determine. If you get this, please share with me \"x\"!\n\nThis would only be problematic for the content object(s) that experience this.\n\n\u003e Server \"x\" is unreachable\n\nOccurs during the gathering content objects stage and trying to build the AllPaths property.\n\nThe server \"x\" that hosts the shared folder is unreachable; it failed a ping test.\n\nThis would only be problematic for the content object(s) that experience this.\n\n\u003e Could not query Win32_Share on \"x\" (y)\n\nOccurs during the gathering content objects stage and trying to build the AllPaths property.\n\nCould not query the Win32_Share WMI class on the server \"x\" that hosts the shared folder. \"y\" is the exception message.\n\nThis would only be problematic for the content object(s) that have source path associated with this server and any of its shared folders.\n\n\u003e Could not resolve share \"x\" on \"y\", either because it does not exist or could not query Win32_Share on server\n\nOccurs during the gathering content objects stage and trying to build the AllPaths property.\n\nCould not determine the local path of shared folder \"x\" because that information could not be retrieved from server \"y\", either because the share does not exist or could not query the Win32_Share class on \"y\".\n\nThis would only be problematic for the content object(s) that experience this.\n\n\u003e Couldn't determine path type for \"x\" so might have problems accessing folders that breach MAX_PATH limit, quitting...\n\nOccurs during the gathering folders stage so we can prefix `-SourcesLocation` with `\\\\?\\..` to workaround the 260 MAX_PATH limit.\n\nThis means that `-SourcesLocation` was in such bananas format that I could not determine it. Please share \"x\" with me if you experience this!\n\nThis is a terminating error. It's important for the script to be able to successfully gather all folders from `-SourcesLocation`.\n\n\u003e Consider using -AltFolderSearch, quiting...\n\nOccurs during the gathering folders stage.\n\nSomething went wrong trying to get all folders under `-SourcesLocation`.\n\nThis is a terminating error. It's important for the script to be able to successfully gather all folders from `-SourcesLocation`.\n\n\u003e Couldn't reset \"x\"\n\nOccurs during the gathering folders stage so we can prefix `-SourcesLocation` with `\\\\?\\..` to workaround the 260 MAX_PATH limit.\n\nAfter the script has finished gathering folders it tries to remove the `\\\\?\\..` prefix. If you experience this when this removal of the prefix failed for some reason. Please share \"x\" with me if you experience this!\n\nThis should not impact the results.\n\n\u003e Won't be able to determine unused folders with given local path while running remotely from site server, quitting\n\nOccurs before gathering any content objects or folders.\n\nYou have given local path for `-SourcesLocation`. We need to ensure we don't produce false positives where a similar folder structure exists on the remote machine and site server.\n\nThis is a terminating error.\n\n\u003e Unable to import ImportExcel module: \"x\"\n\nOccurs before gathering any content objects or folders.\n\nYou have specified the switch to produce Excel report but do not have the [ImportExcel](https://github.com/dfinke/ImportExcel) module installed.\n\nThis is a terminating error.\n\n\u003e Failed to export PowerShell object: \"x\"\n\nOccurs after main execution and just before closing.\n\nYou have specified the switch to export either all ConfigMgr content objects or the result object, and there was a problem doing so.\n\nThe script will attempt to continue and close normally.\n\n\u003e Failed to create Excel report: \"x\"\n\nOccurs after main execution and just before closing.\n\nYou have specified the switch to produce a Excel report and there was a problem doing so.\n\nThe script will attempt to continue and close normally.\n\n## Parameters\n\n### -SourcesLocation (mandatory)\n\nThe path to the directory you store your ConfigMgr sources. Can be a UNC or local path. Must be a valid path that you have read access to.\n\n### -SiteServer (mandatory)\n\nThe site server of the given ConfigMgr site code. The server must be reachable over a network.\n\n### -SiteCode\n\nThe site code of the ConfigMgr site you wish to query for content objects.\n\n### -ExcludeFolders\n\nAn array of folders that you want to exclude the script from checking, which should be absolute paths under the path given for -SourcesLocation.\n\n### -Packages\n\nSpecify this switch to include Packages within the search to determine unused content on disk.\n\n### -Applications\n\nSpecify this switch to include Applications within the search to determine unused content on disk.\n\n### -Drivers\n\nSpecify this switch to include Drivers within the search to determine unused content on disk.\n\n### -DriverPackages\n\nSpecify this switch to include DriverPackages within the search to determine unused content on disk.\n\n### -OSImages\n\nSpecify this switch to include OSImages within the search to determine unused content on disk.\n\n### -OSUpgradeImages\n\nSpecify this switch to include OSUpgradeImages within the search to determine unused content on disk.\n\n### -BootImages\n\nSpecify this switch to include BootImages within the search to determine unused content on disk.\n\n### -DeploymentPackages\n\nSpecify this switch to include DeploymentPackages within the search to determine unused content on disk.\n\n### -AltFolderSearch\n\nSpecify this if you suspect there are issue with the default mechanism of gathering folders, which is:\n\n```powershell\nGet-ChildItem -LiteralPath \"\\\\?\\UNC\\server\\share\\folder\" -Directory -Recurse | Select-Object -ExpandProperty FullName\n```\n\n### -NoProgress\n\nSpecify this to disable use of Write-Progress.\n\n### -Log\n\nSpecify this to enable logging. The log file(s) will be saved to the same directory as this script with a name of `\u003cscriptname\u003e_\u003cdatetime\u003e.log`. Rolled log files will follow a naming convention of `\u003cfilename\u003e_1.lo_` where the int increases for each rotation. Each maximum log file is 2MB.\n\n### -ExportReturnObject\n\nSpecify this option if you wish to export the PowerShell result object to an XML file. The XML file be saved to the same directory as this script with a name of `\u003cscriptname\u003e_\u003cdatetime\u003e_result.xml`. It can easily be reimported using `Import-Clixml` cmdlet.\n\n### -ExportCMContentObjects\n\nSpecify this option if you wish to export all ConfigMgr content objects to an XML file. The XML file be saved to the same directory as this script with a name of `\u003cscriptname\u003e_\u003cdatetime\u003e_cmobjects.xml`. It can easily be reimported using `Import-Clixml` cmdlet.\n\n### -ExcelReport\n\nSpecify this option to enable the generation for an Excel report of the result. Doing this will force you to have the ImportExcel module installed. For more information on ImportExcel: https://github.com/dfinke/ImportExcel. The .xlsx file will be saved to the same directory as this script with a name of \u003cscriptname\u003e_\u003cdatetime\u003e.xlsx.\n\n### -Threads\n\nSet the number of threads you wish to use for concurrent processing of this script. Default value is number of processes from environment variable `NUMBER_OF_PROCESSORS`.\n\n## Author\n\nAdam Cook\n\nTwitter: [@codaamok](https://twitter.com/codaamok)\n\nWebsite: https://www.cookadam.co.uk\n\n## License\n\nPlease read the LICENSE file provided.\n\n## Acknowledgements\n\nBig thanks to folks in [Windows Admins slack](https://slofile.com/slack/winadmins):\n\n- Cody Mathis ([@codymathis123](https://github.com/CodyMathis123))\n- Chris Kibble ([@ChrisKibble](https://github.com/ChrisKibble))\n- Chris Dent ([@indented-automation](https://github.com/indented-automation))\n- Kevin Crouch ([@PsychoData](https://github.com/PsychoData))\n- Patrick Seymour ([@pseymour](https://github.com/pseymour))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodaamok%2Fget-cmunusedsources","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodaamok%2Fget-cmunusedsources","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodaamok%2Fget-cmunusedsources/lists"}