Imagine yourself managing a sprawling Dynamics 365 solution, bloated with thousands of components, including web resources, entities, and plugins, many of which are unused web resources with no dependencies. Cleaning them up via the Power Apps maker portal (https://make.powerapps.com/) means scrolling through endless lists and manually deleting each one, a tedious and error-prone task. One wrong move could break dependencies, crashing your solution. This common headache for Dynamics 365/Dataverse developers and admins boils down to two challenges:
- Manually reviewing and deleting thousands of components through the UI is time-consuming and error-prone.
- Identifying unreferenced components to avoid breaking dependencies (e.g., linked to forms or processes) is risky and disruptive.
Here comes our tech-saviors, the powerful duo of RemoveSolutionComponentRequest and RetrieveDependenciesForDeleteRequest from Microsoft.Crm.Sdk.Messages, your cleanup superheroes.
These SDK classes work together to programmatically target and remove unreferenced components from an unmanaged solution, with dependency checks to ensure safety. In this blog, you’ll learn how to streamline solution cleanup with key code snippets, components, and benefits for efficient maintenance.
Approach
The aim is to replace manual UI deletions with an automated script that identifies solution components in SampleSolution (e.g., web resources with no dependencies) and removes them safely.
The process uses a QueryExpression to target web resources (ComponentType=61) and checks dependencies to ensure only idle components are deleted.
The steps include:
- Connecting to Dataverse using CrmServiceClient.
- Querying solutioncomponent and linked webresource entities to find components.
- Validating dependencies with RetrieveDependenciesForDeleteRequest.
- Removing unreferenced components with RemoveSolutionComponentRequest.
- Logging actions and pausing the console for debugging.
This approach eliminates manual effort, ensures safety, and scales for large solutions.
Step-by-Step Guide
This guide provides a practical walkthrough to set up a console script that cleans up unreferenced web resources in Dynamics 365/Dataverse. We’ll focus on web resources with no dependencies, ensuring only those with no dependencies are removed. Set up an Azure AD app registration for authentication (ClientId, ClientSecret, org URL)
Step 1: Query Web Resources
Query solutioncomponent for web resources (ComponentType=61) in SampleSolution.
string solutionUniqueName = "SampleSolution"; int componentType = 61; // Web Resource QueryExpression query = new QueryExpression("solutioncomponent") { ColumnSet = new ColumnSet("objectid", "componenttype"), Criteria = new FilterExpression { Conditions = { new ConditionExpression("solutionid", ConditionOperator.Equal, GetSolutionId(service, solutionUniqueName)), new ConditionExpression("componenttype", ConditionOperator.Equal, componentType) } }, LinkEntities = { new LinkEntity { LinkFromEntityName = "solutioncomponent", LinkToEntityName = "webresource", LinkFromAttributeName = "objectid", LinkToAttributeName = "webresourceid", Columns = new ColumnSet("name"), EntityAlias = "wr", } } }; var results = service.RetrieveMultiple(query); LogMessage($"Found {results.Entities.Count} components matching query.");
Step 2: Check Dependencies
For each component, use RetrieveDependenciesForDeleteRequest to confirm no dependencies exist.
foreach (var component in results.Entities) { string componentName = component.GetAttributeValue<AliasedValue>("wr.name")?.Value?.ToString(); if (componentName == null) continue; LogMessage($"Processing component: {componentName} ({component.Id})"); // Check dependencies var dependencyRequest = new RetrieveDependenciesForDeleteRequest { ComponentType = componentType, ObjectId = component.GetAttributeValue<Guid>("objectid") }; var dependencyResponse = (RetrieveDependenciesForDeleteResponse)service.Execute(dependencyRequest); if (dependencyResponse.EntityCollection.Entities.Count == 0) { LogMessage($"{componentName} has no dependencies. Attempting removal..."); } else { LogMessage($"Skipped {componentName}: Has {dependencyResponse.EntityCollection.Entities.Count} dependencies."); } }
Step 3: Remove Idle Components
Remove components with no dependencies using RemoveSolutionComponentRequest.
if (dependencyResponse.EntityCollection.Entities.Count == 0) { var removeRequest = new RemoveSolutionComponentRequest { ComponentId = component.GetAttributeValue("objectid"), ComponentType = componentType, SolutionUniqueName = solutionUniqueName }; try { service.Execute(removeRequest); LogMessage($"Successfully removed: {componentName} ({component.Id})"); } catch (Exception ex) { LogMessage($"Error removing {componentName}: {ex.Message}"); } }
Key Components
- RemoveSolutionComponentRequest:
- ComponentId: Guid of the component (e.g., a3912691-378e-f011-b4cc-6045bda590e0).
- ComponentType: Integer code (e.g., 61 for web resources).
- SolutionUniqueName: Name of the unmanaged solution (e.g., SampleSolution).
- RetrieveDependenciesForDeleteRequest:
- ComponentType: Matches the type above.
- ObjectId: Guid of the component to check.
- RetrieveDependenciesForDeleteResponse:
- EntityCollection: Lists dependencies; empty means the component is unreferenced and safe to remove
Output
The script logs actions to debug_log.txt and the console, detailing processed and removed components.
For example, consider two web resources in SampleSolution: onLoadofAccount (attached to the Account form’s onLoad event, dependent on the Account entity) and Test Account Script (an idle script with no dependencies). The script identifies Test Account Script as unreferenced and removes it, while skipping onLoadofAccount due to its dependency.
Before Cleanup: SampleSolution in make.powerapps.com, showing onLoadofAccount with its dependency and Test Account Script under Web Resources.
After Cleanup: SampleSolution with only onLoadofAccount remaining, as Test Account Script was removed.
Conclusion
Automating solution cleanup with RemoveSolutionComponentRequest and RetrieveDependenciesForDeleteRequest transforms a tedious and error-prone process into an efficient and safe workflow. By specifically targeting unreferenced web resources with a QueryExpression and ensuring dependency checks, this approach:
- Saves hours by eliminating manual deletions in large solutions.
- Prevents errors by only removing idle components with no dependencies.
- Adapts easily to other component types (e.g., ComponentType=1 for entities)
Give this approach a try in your Dynamics 365 solution and feel free to share your experience or insights in the comments!