The inner loop is where developers spend most of their time - writing code, testing changes, and iterating quickly. In Power Platform, your solution is your source code. This lab focuses on working in the inner loop with your Power Platform solution, exploring how solutions exist at different levels of abstraction (binary packages, unpacked metadata files, and traditional source code projects), and how Power Platform Git Integration enables version control collaboration. Youâll learn how to handle conflicts when changes occur in multiple places, work across development environments, and integrate AI agents into your ALM workflow. This hands-on lab demonstrates the complete inner-loop lifecycle of managing Power Platform solutions.
â Lab tasks
In this lab, you will go through the following tasks:
For low-code makers, your Power Platform Solution is your source code - itâs the complete description of what youâre building that you can open and edit in the Solution Explorer within the maker portal.
The term âsolutionâ can be confusing because it represents different levels of abstraction depending on the context:
.msapp, .xml, .js, .dll files
Note: Itâs not possible to generate traditional source code from runtime artifacts in a solution. Traditional source code must be maintained in a repository by the developer as part of their development workflow.
Key Insight: All three formats represent the same logical solution, just at different levels of abstraction. Power Platform Git Integration covers only the unpacked solution - it enables version control, collaboration, and deployment of low-code components. Traditional source code (like C# plugins, TypeScript PCF components, and project files) should be managed by traditional source code tools and development workflows alongside Power Platform components in your repository.
To complete this lab, you need to have the following environments and resources configured:
| Environment Name | Environment Type | Purpose |
|---|---|---|
| Dev | Development | Primary development environment for creating and testing solutions |
| HotFix | Development | Secondary environment for hotfix development and multi-environment testing |
In this task, we will work with the Solution binary package (.zip format) as defined earlier in our abstractions. This is the packaged format used for importing and exporting solutions between Power Platform environments.


assets folder in this repository.zip file provided for this lab



note: This step requires Power Platform CLI installed in a windows machine.
The Contoso Real Estate solution contains the schema and components (tables, apps, flows) but does not include sample data. If you would like to populate your environment with configuration data for testing purposes, you can import the data using PAC CLI.
To import the data, you can use either:
pac data import -d [file]
or use the Configuration Migration Tool:
pac tool CMT


Note: if you donât have an organization to select you must create an Azure DevOps Organization.

You are now connect to your GIT repository!

Important: After setting up Git integration, all the source control components will be processed in the background. During this time, the commit functionality will be disabled. Wait until this process completes before attempting to commit. Youâll know itâs ready when the commit button becomes active.

Expected outcome: Your solution is now imported into your development environment and committed to source control as an unpacked solution format.
In this task, we will work with the Solution Explorer in the Power Apps maker portal as our reference for source code. Youâll learn how changes flow between the maker portal and your Git repository, including how to handle conflicts.

Screens:
scrALM:
Children:
- ScreenContainer4:
Control: GroupContainer@1.3.0
Variant: AutoLayout
Properties:
Fill: =RGBA(245, 245, 245, 1)
Height: =Parent.Height
LayoutAlignItems: =LayoutAlignItems.Stretch
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =16
PaddingBottom: =16
PaddingLeft: =16
PaddingRight: =16
PaddingTop: =16
Width: =Parent.Width
Children:
- MainContainer3:
Control: GroupContainer@1.3.0
Variant: AutoLayout
Properties:
Fill: =RGBA(255, 255, 255, 1)
LayoutDirection: =LayoutDirection.Vertical
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Children:
- LinkCanvas1:
Control: Link@0.0.45
Properties:
Align: =Align.Center
AlignInContainer: =AlignInContainer.Stretch
Appearance: ='LinkCanvas.Appearance'.Default
AutoHeight: =true
FillPortions: =1
FontSize: =52
LayoutMinHeight: =72
Text: ="aka.ms/PPCC25-ALM"
URL: ="https://aka.ms/PPCC25-ALM"
VerticalAlign: =VerticalAlign.Middle
scrCommunityYAML:
Children:
- ScreenContainer3:
Control: GroupContainer@1.3.0
Variant: AutoLayout
Properties:
Fill: =RGBA(245, 245, 245, 1)
Height: =Parent.Height
LayoutAlignItems: =LayoutAlignItems.Stretch
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =16
PaddingBottom: =16
PaddingLeft: =16
PaddingRight: =16
PaddingTop: =16
Width: =Parent.Width
Children:
- HeaderContainer2:
Control: GroupContainer@1.3.0
Variant: AutoLayout
Properties:
Fill: =RGBA(255, 255, 255, 1)
FillPortions: =0
Height: =75
LayoutDirection: =LayoutDirection.Horizontal
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Children:
- TextCanvas3_2:
Control: Text@0.0.51
Properties:
Height: =40
Size: =24
Text: "=If(\n Hour(Now()) >= 6 && Hour(Now()) < 12, \"\U0001F324ïž Good Morning, \",\n Hour(Now()) >= 12 && Hour(Now()) < 18, \"âïž Good Afternoon, \",\n \"\U0001F319 Good Night, \")\n & Left(User().FullName, Find(\" \", User().FullName) - 1)\n "
Weight: ='TextCanvas.Weight'.Semibold
Width: =500
- MainContainer2:
Control: GroupContainer@1.3.0
Variant: AutoLayout
Properties:
Fill: =RGBA(255, 255, 255, 1)
LayoutDirection: =LayoutDirection.Vertical
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Children:
- LoaingImage:
Control: Image@2.2.3
Properties:
DisplayMode: =DisplayMode.View
Fill: =RGBA(56, 96, 178, 0.1)
Height: =Parent.Height
Image: |
=$"data:image/svg+xml;utf-8, {EncodeUrl("<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%' viewBox='0 0 100 100'>
<style>
.loading-text {
font-family: Arial, sans-serif;
font-size: 5px;
font-weight: bold;
fill: red;
text-anchor: middle;
}
.spinner {
animation: rotate 2s linear infinite;
transform-origin: 50px 50px;
}
@keyframes rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.track {
fill: none;
stroke: #ccc;
stroke-width: 3;
}
.circle {
fill: none;
stroke: #010069;
stroke-width: 3;
stroke-linecap: round;
stroke-dasharray: 45, 100;
stroke-dashoffset: 0;
animation: dash 1.5s ease-in-out infinite;
}
@keyframes dash {
0% { stroke-dasharray: 1, 100; stroke-dashoffset: 0; }
50% { stroke-dasharray: 45, 100; stroke-dashoffset: -25; }
100% { stroke-dasharray: 45, 100; stroke-dashoffset: -75; }
}
</style>
<!-- Bold red loading text ABOVE the circle -->
<text x='50' y='25' class='loading-text'>Loading, please wait</text>
<!-- Smaller static circle track -->
<circle class='track' cx='50' cy='50' r='15'></circle>
<!-- Smaller spinning progress circle -->
<g class='spinner'>
<circle class='circle' cx='50' cy='50' r='15'></circle>
</g>
</svg>")}"
Transparency: =
Width: =Parent.Width
- FooterContainer1:
Control: GroupContainer@1.3.0
Variant: AutoLayout
Properties:
Fill: =RGBA(255, 255, 255, 1)
FillPortions: =0
Height: =75
LayoutDirection: =LayoutDirection.Horizontal
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Children:
- cntCurrentTime:
Control: GroupContainer@1.3.0
Variant: ManualLayout
Properties:
DropShadow: =DropShadow.None
Height: =97
RadiusBottomLeft: =0
RadiusBottomRight: =0
RadiusTopLeft: =0
RadiusTopRight: =0
Width: =333
X: =363
Y: =318
Children:
- lblCurrentTime:
Control: Label@2.5.1
Properties:
Align: =Align.Center
Color: =RGBA(9, 33, 98, 1)
FontWeight: =FontWeight.Semibold
Height: =Parent.Height
Size: =19
Text: |
="Current Time: " & Text(nowTime, "h:mm AM/PM", "en-US")
Width: =Parent.Width
- tmrCurrentTime:
Control: Timer@2.1.0
Properties:
AutoPause: =false
AutoStart: =true
Duration: =1000
OnTimerStart: =Set(nowTime, Now())
Repeat: =true
Visible: =false
Select any screen to paste the YAML snippet.

Two new screens should be added. scrALM and scrCommunityYAML

Now that youâve made changes to your canvas app, youâll save those changes locally, publish them to make them live, and then commit them to your Git repository to preserve the changes in source control.




Key takeaway: You have successfully used the maker portal to commit and update your source code in Azure DevOps. Your changes are now version controlled and available for collaboration.
Professional developers often use specialized tools to compare, review, and make changes directly to the repository. In this step, weâll use VS Code (via vscode.dev) to demonstrate how developers can work with the unpacked solution files directly in source control, making targeted changes to YAML files without going through the maker portal.




Important concepts:




DONâT COMMIT (we want to keep the repository out of sync)

Now, go back to maker portal, open your solution and Check for updates from your repo.

Scenario: Youâve now simulated a real-world development situation where two developers are working on the same solution simultaneously:
This demonstrates why maintaining a 1:1 relationship between environment and branch is critical - two developers should not work on the same branch, as changes made in parallel can conflict. Now weâll see how to bring the developerâs changes back into the maker portal.
Expected outcome: You understand how to work with both maker portal and repository, handle reverse sync, and resolve conflicts by choosing a source of truth. Key learning: More complex merge scenarios and team collaboration patterns will be covered in the next lab.
In this task, we will simulate team development using multiple Power Platform environments. This demonstrates how different developers can work on the same solution using separate development environments while maintaining code synchronization through Git.
Prerequisites: Youâll need access to a second development environment for this exercise. We will call this environment Hotfix
.zip file to your computer.zip file you just exported from your Dev environmentNote: Youâre importing the exported solution from your Dev environment which includes all your recent changes. This allows you to have a consistent starting point in the HotFix environment for testing multi-environment workflows.
Now weâll create a new Git branch from the main branch to work in an isolated environment. This simulates a hotfix scenario where you need to make urgent production fixes without impacting ongoing development work.





Important concept: In Power Platform, we recommend maintaining a 1:1 relationship between environment and branch. Each development environment should be connected to its own dedicated Git branch for clean separation of work.
In this step, youâll simulate a hotfix scenario by making a critical update to the canvas app in your HotFix environment. Since the HotFix branch is isolated from your main development branch, you can work independently and commit changes without affecting ongoing development work.








Expected outcome: Youâve successfully created an isolated hotfix in a separate branch, demonstrating how to handle urgent production fixes without impacting ongoing development work. Key learning: The 1:1 environment-to-branch relationship keeps development organized and makes it easier to track which changes come from which developer or environment.
In this final task, we will create a Microsoft Copilot Studio (MCS) agent and configure it to use Dataverse tables as a knowledge source. This demonstrates how AI agents integrate into the Power Platform ALM lifecycle alongside apps and flows. To make the agent ALM-ready, weâll create it within a solution from the start.
Look at your browserâs URL - it should look like:
https://make.powerapps.com/environments/12345678-1234-1234-1234-123456789abc/apps
/environments/ and /apps)https://make.powerapps.com/environments/12345678-1234-1234-1234-123456789abc/apps, your environment ID is 12345678-1234-1234-1234-123456789abcNavigate to Copilot Studio using your environment ID:
https://copilotstudio.microsoft.com/environments/YOUR-ENVIRONMENT-ID-HERE
YOUR-ENVIRONMENT-ID-HERE with the environment ID you copied from Power Appshttps://copilotstudio.microsoft.com/environments/12345678-1234-1234-1234-123456789abcNote: If you donât see the environment ID in the Copilot Studio URL, you may not be in the correct environment. Go back to Power Apps, copy the environment ID again, and update the Copilot Studio URL accordingly.

To make your agent ALM-ready, it must be created within a solution. This enables version control, deployment across environments, and integration with Git.


Important: The solution must be connected to Git integration to enable source control for your agent. This allows you to commit agent changes alongside other solution components.

âI want to build an agent that helps real estate agents to find properties. When the agent describes a property, the agent will ask for specific features and a budget.â

Configure the agent
The conversational creation experience with Copilot will next load. Youâll see Copilot is in progress of responding to you.





Note: To test the agent effectively, you need data in the Listing table. You can either:
- Import sample data using the optional data import step from Task 1, or
- Create your own data using the Contoso Real Estate Canvas App or Model-Driven App included in the solution

Note: You can continue to refine the agent and add more components, but for this lab we will focus on the ALM aspect of it. If you want to explore how to build Microsoft Copilot Studio agents in depth, check out the Agent Academy.
Important: Normally, you must publish an agent before it can be committed to source control. However, for this lab, we will skip the publish step and commit the agent directly to demonstrate the ALM workflow.



Expected outcome: You have successfully created a Copilot Studio agent within a solution, connected it to Dataverse tables as a knowledge source, and committed it to source control as part of your ALM workflow.
Key insights:
- Creating agents within solutions from the start makes them ALM-ready
- Agents follow the same source control patterns as apps, flows, and other solution components
- AI agents are now first-class citizens in Power Platform ALM
Key insights:
- Creating agents within solutions from the start makes them ALM-ready
- Agents follow the same source control patterns as apps, flows, and other solution components
- AI agents are now first-class citizens in Power Platform ALM