Last active
March 22, 2025 14:41
Code-first low-code demo VS Code workspace snippets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
// Hello! This is my demo script for setting up a new repository for a Power Platform project. | |
// See my recorded demo sessions on our YouTube channel: https://www.youtube.com/playlist?list=PLFCzz03beGm5cthgn7LZh4bt-d9g1G6ip | |
// | |
// DEMO SETUP INSTRUCTIONS | |
// | |
// 1. Install Visual Studio Code (VS Code) from https://code.visualstudio.com/" | |
// | |
// 2. Install PowerShell (macOS) since this demo and tools are using PowerShell scripts | |
// dotnet tool install --global "PowerShell" | |
// | |
// 3. Open Terminal in VS Code by pressing cmd+J (Mac) or ctrl+` (Windows) or selecting Terminal -> New Terminal from the menu | |
// | |
// 4. Execute 'pwsh' in the terminal to start PowerShell | |
// | |
// 5. Create a working folder where the repository will be initialized, open it in VS Code, create .vscode and .demo folders: | |
// mkdir CodeFirstPowerPlatformDemo | |
// code CodeFirstPowerPlatformDemo | |
// mkdir .vscode | |
// mkdir .demo | |
// | |
// 6. Download the snippet file to your working folder: | |
// Invoke-WebRequest -Uri 'https://gist.githubusercontent.com/TomProkop/607a9de00d811a5ae68327e90f6a81cf/raw' -OutFile '.vscode/CodeFirstPowerPlatformDemo.code-snippets' | |
// | |
// 7. Create a PowerShell script file where you'll be executing the commands from this demo: | |
// New-Item -ItemType File -Name '.demo/DemoScriptPad.ps1' | |
// | |
// 8. Open the PowerShell script file in VS Code and start typing 'CFA00' to see the first command: | |
// code .demo/DemoScriptPad.ps1 | |
// | |
// 9. Make the currenty opened PowerShell script run after cleaning terminal by pressing F5 | |
// $keybinding = @' | |
// [ | |
// { | |
// "key": "f5", | |
// "command": "runCommands", | |
// "args": { | |
// "commands": [ | |
// "workbench.action.terminal.clear", | |
// "PowerShell.Debug.Start" | |
// ] | |
// }, | |
// "when": "editorTextFocus && editorLangId == 'powershell'" | |
// } | |
// ] | |
// '@; $path = ".vscode/keybindings.json"; if (-not (Test-Path ".vscode")) { mkdir .vscode }; Set-Content -Path $path -Value $keybinding | |
// | |
// 10. You can run parts of the script by selecting the code and pressing F8 or run the whole script by pressing F5 | |
// | |
// 11. Use cmd+J to open/close the terminal | |
{ | |
"Power Platform Repository Demo Script Intro": { | |
"scope": "powershell", | |
"prefix": "CFA00-machine-setup", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA00: Power Platform Repository Demo Script Intro ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# This is a demo script to show how to set up a new repository for a Power Platform project.", | |
"# Many of the steps are usually carried out in IDE or web browser, but for the sake of", | |
"# the demo, we'll try to use the command line as much as possible to explain the process.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Prerequisites", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# 1. Install Visual Studio Code (VS Code) from https://code.visualstudio.com/", | |
"", | |
"# 2. Install the .NET SDK", | |
"# https://dotnet.microsoft.com/en-us/download", | |
"", | |
"# 3. Install PowerShell (macOS) since this demo and tools are using PowerShell scripts.", | |
"dotnet tool install --global \"PowerShell\"", | |
"", | |
"# 4. Install PowerShell VS Code extension:", | |
"# Open extension page from https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell", | |
"", | |
"# 5. Instal Cucumber VS Code extenstion for Gherkin test authoring:", | |
"# Open extension page from https://marketplace.visualstudio.com/items?itemName=CucumberOpen.cucumber-official", | |
"", | |
"# 6. If you haven't used Git before, you need to install it and set your user name and email.", | |
"# Download installation from https://git-scm.com/downloads", | |
"git config --global user.name \"${2:Tomas Prokop}\"", | |
"git config --global user.email \"${3:tomas.prokop@REDACTED.org}\"", | |
"", | |
"# 7. Configure NuGet to use the public package feed (might be already done by the SDK install)", | |
"dotnet nuget add source \"https://api.nuget.org/v3/index.json\" -n \"nuget.org\"", | |
"", | |
"# 8. Install Azure CLI. More info: aka.ms/azcli-docs", | |
"# Windows:", | |
"winget install -e --id \"Microsoft.AzureCLI\"", | |
"# macOS:", | |
"brew install azure-cli", | |
"", | |
"# 9. Install Azure DevOps CLI extension", | |
"az extension add --name \"azure-devops\"", | |
"", | |
"# 10. Install Power Platform CLI (pac). More info: https://aka.ms/PowerPlatformCLI", | |
"# A) Install the Visual Studio Code extension (available in VS Code terminal)", | |
"# https://marketplace.visualstudio.com/items?itemName=microsoft-IsvExpTools.powerplatform-vscode", | |
"", | |
"# B) Install with .NET Tool (modern cross-platform CLI which doesn't support PD and CMT yet)", | |
"dotnet tool install --global \"Microsoft.PowerApps.CLI.Tool\"", | |
"", | |
"# C) Install with Windows MSI (Windows only)", | |
"# https://aka.ms/PowerAppsCLI" | |
] | |
}, | |
"Initialize a new Git repository and create branches": { | |
"scope": "powershell", | |
"prefix": "CFA01-git-init", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA01: Initialize a new Git repository and create branches ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# Git is a distributed version control system that allows multiple developers to work on", | |
"# the same project simultaneously. It tracks changes in the codebase, allows for collaboration", | |
"# and provides a history of changes.", | |
"#", | |
"# A branch is a parallel version of the codebase that allows developers to work on features", | |
"# or bug fixes without affecting the main codebase. Branches can be created, merged and deleted.", | |
"#", | |
"# Power Platform projects based on Dataverse can be versioned in Git repositories thanks to", | |
"# solutions framework which allows exporting and importing components.", | |
"#", | |
"# We will adopt a trunk-based branching strategy, where all developers collaborate on the main", | |
"# branch, resisting the creation of long-lived development branches which lead to merge hells.", | |
"#", | |
"# The '${1:main}' branch will serve as the integration point for all work.", | |
"# Each feature or bug fix will be implemented on short-lived topic branches.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Initialize the Git repository with '${1:main}' as the default branch", | |
"git init -b \"${1:main}\"", | |
"", | |
"# Step 2: Create an empty README file and commit it to the main branch to initialize the repository", | |
"New-Item -Path README.md -ItemType File", | |
"git add README.md", | |
"git commit -m \"Initial commit\"", | |
"", | |
"# Step 3: To fix a bug or implement a feature, a developer creates a short-lived", | |
"# new branch off the main integration branch.", | |
"git checkout -b \"${2:users/tomas.prokop/project-setup}\"" | |
] | |
}, | |
"Create repository root files and folders": { | |
"scope": "powershell", | |
"prefix": "CFA02-create-repo-root-file-folders", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA02: Create repository root files and folders ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# A monorepo is a single repository that contains multiple projects, components or apps.", | |
"# It's a simplified approach for components which are tightly integrated or frequently ", | |
"# updated together.", | |
"#", | |
"# When repositories are split, the overhead of managing dependencies, versioning ", | |
"# and artifact stores increases, which may not add value unless you're building reusable ", | |
"# components for multiple customer projects. This reduces the complexity of coordinating ", | |
"# dependent pull requests (PRs) and ensures that code reviews provide full context. ", | |
"# Additionally, maintaining CI/CD pipelines is simpler, as orchestration is consolidated ", | |
"# in one place instead of across multiple repositories.", | |
"#", | |
"# MSBuild and .NET project system helps to manage multiple projects in a single repository.", | |
"# Visual Studio solution files (.sln) are used to keep track of the projects.", | |
"#", | |
"# A consistent folder structure in monorepos is essential because it keeps things simple ", | |
"# and predictable. Without it, teams tend to isolate their projects in deep, nested directories,", | |
"# which makes collaboration harder and code reuse less likely.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Generate a .gitignore file to exclude unnecessary files from being committed to Git.", | |
"# dotnet new template contains common rules for .NET projects.", | |
"dotnet new gitignore", | |
"", | |
"# Step 2: Create a new Visual Studio solution file (not a Dataverse solution).", | |
"dotnet new sln --name ${1:InventoryManagement}", | |
"", | |
"# Step 3: Create a `src` directory to hold the source code for your project(s).", | |
"mkdir src", | |
"", | |
"# Step 4: For demonstration purposes, exclude the '.demo' and '.vscode' folders ", | |
"# from version control by adding it to `.gitignore`.", | |
"Add-Content .gitignore \".demo/\"", | |
"Add-Content .gitignore \".vscode/\"" | |
] | |
}, | |
"Create a project with UI tests": { | |
"scope": "powershell", | |
"prefix": "CFA03-create-ui-tests", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA03: Create a project for UI tests ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# This script creates an application in which you can write tests in the Gherkin language. ", | |
"# When creating this application from template, you can automatically initialize user secrets, ", | |
"# connect the FluentAssertions library, ", | |
"# choose a framework to test your application (MStest, NUnit, XUnit ) ", | |
"# and generate appsetings.json to configure your tests ", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"#", | |
"# Create project from template", | |
"#", | |
"dotnet new pp-test-ui `", | |
"--output \"src/Tests.UI\" `", | |
"--createAppsetingsFile true `", | |
"--enableUserSecrets true `", | |
"--testExecutionFramework \"mstest\" `", | |
"--includeFluentAssertions true `", | |
"--enableCucumberSupport true `", | |
"--allow-scripts yes", | |
"#", | |
"#", | |
"# Add .feature file to project", | |
"dotnet new pp-test-ui-feature `", | |
"--output \"src/Tests.UI\" " | |
] | |
}, | |
"Create a Dataverse solution project for a database schema": { | |
"scope": "powershell", | |
"prefix": "CFA04-create-model-solution", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA03: Create a Dataverse solution project for a database schema ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# We will use the .NET templating engine (dotnet new) to create a Dataverse solution project.", | |
"# The .NET templating engine is a system that allows us to define project templates", | |
"#", | |
"# Unlike the Power Platform CLI, which generates projects that require post-processing to be ", | |
"# compatible with .NET project system, this template from the TALXIS.DevKit.Templates.Dataverse", | |
"# package resolves those issues.", | |
"#", | |
"# You can check out how the templates are built and used:", | |
"# https://github.com/TALXIS/tools-devkit-templates", | |
"#", | |
"# Templates automatically add new projects to the .sln file.", | |
"# If you wanted to do it manually you would use the following command:", | |
"# dotnet sln add src/Solutions.DataModel", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Install the TALXIS.DevKit.Templates.Dataverse NuGet package, which contains", | |
"# templates for creating common Power Platform components.", | |
"dotnet new install TALXIS.DevKit.Templates.Dataverse", | |
"", | |
"# Step 2: Create a solution project for the database schema using the installed template.", | |
"dotnet new pp-solution `", | |
"--output \"src/${1:Solutions.DataModel}\" `", | |
"--PublisherName \"${2:tomas}\" `", | |
"--PublisherPrefix \"${3:tom}\" `", | |
"--allow-scripts yes" | |
] | |
}, | |
"Create database tables in the model solution": { | |
"scope": "powershell", | |
"prefix": "CFA05-create-tables", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA04: Create new tables in Dataverse model solution ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# We will create new tables in the Dataverse solution for tracking items and transactions.", | |
"# Dataverse supports multiple table types and in this case, we are adding a Standard table.", | |
"#", | |
"# There is a difference between adding all table metadata to a solution and adding a table", | |
"# reference to a solution. The reference allows adding columns, views, forms, and ribbon buttons.", | |
"# This is controlled by the --Behavior argument, which can be set to either 'New' or 'Existing'.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Add Warehouse Item table to the solution", | |
"dotnet new pp-entity `", | |
"--output \"src/${1:Solutions.DataModel}\" `", | |
"--EntityType \"${2:Standard}\" `", | |
"--Behavior \"${3:New}\" `", | |
"--PublisherPrefix \"${4:tom}\" `", | |
"--LogicalName \"${5:warehouseitem}\" `", | |
"--LogicalNamePlural \"${6:warehouseitems}\" `", | |
"--DisplayName \"${7:Warehouse Item}\" `", | |
"--DisplayNamePlural \"${8:Warehouse Items}\" `", | |
"--SolutionRootPath \"${9:Declarations}\" `", | |
"--allow-scripts yes", | |
"", | |
"# Step 2: Add Warehouse Transaction table to the solution", | |
"dotnet new pp-entity `", | |
"--output \"src/${1:Solutions.DataModel}\" `", | |
"--EntityType \"${2:Standard}\" `", | |
"--Behavior \"${3:New}\" `", | |
"--PublisherPrefix \"${4:tom}\" `", | |
"--LogicalName \"${10:warehousetransaction}\" `", | |
"--LogicalNamePlural \"${11:warehousetransactions}\" `", | |
"--DisplayName \"${12:Warehouse Transaction}\" `", | |
"--DisplayNamePlural \"${13:Warehouse Transactions}\" `", | |
"--SolutionRootPath \"${9:Declarations}\" `", | |
"--allow-scripts yes" | |
] | |
}, | |
"Add attributes to the tables in the model solution": { | |
"scope": "powershell", | |
"prefix": "CFA06-add-attributes", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA05: Add columns to the previously created tables ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# In Dataverse, tables are made up of columns.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Add Available Quantity column to the Warehouse Item table", | |
"dotnet new pp-entity-attribute `", | |
"--output \"src/${1:Solutions.DataModel}\" `", | |
"--EntitySchemaName \"${2:tom_warehouseitem}\" `", | |
"--AttributeType \"${3:WholeNumber}\" `", | |
"--RequiredLevel \"${4:required}\" `", | |
"--PublisherPrefix \"${5:tom}\" `", | |
"--LogicalName \"${6:availablequantity}\" `", | |
"--DisplayName \"${7:Available Quantity}\" `", | |
"--SolutionRootPath \"${8:Declarations}\" `", | |
"--allow-scripts yes", | |
"", | |
"# Step 2: Add Quantity column to the Warehouse Transaction table", | |
"dotnet new pp-entity-attribute `", | |
"--output \"src/${1:Solutions.DataModel}\" `", | |
"--EntitySchemaName \"${9:tom_warehousetransaction}\" `", | |
"--AttributeType \"${10:WholeNumber}\" `", | |
"--RequiredLevel \"${11:required}\" `", | |
"--PublisherPrefix \"${5:tom}\" `", | |
"--LogicalName \"${12:quantity}\" `", | |
"--DisplayName \"${13:Quantity}\" `", | |
"--SolutionRootPath \"${8:Declarations}\" `", | |
"--allow-scripts yes", | |
"", | |
"# Step 3: Add Item lookup column to the Warehouse Transaction table", | |
"dotnet new pp-entity-attribute `", | |
"--output \"src/${1:Solutions.DataModel}\" `", | |
"--EntitySchemaName \"${14:tom_warehousetransaction}\" `", | |
"--AttributeType \"${15:Lookup}\" `", | |
"--RequiredLevel \"${16:required}\" `", | |
"--PublisherPrefix \"${5:tom}\" `", | |
"--LogicalName \"${17:itemid}\" `", | |
"--DisplayName \"${18:Item}\" `", | |
"--ReferencedEntityName \"${19:tom_warehouseitem}\" `", | |
"--SolutionRootPath \"${8:Declarations}\" `", | |
"--allow-scripts yes" | |
] | |
}, | |
"Initialize a Dataverse solution project for a UI layer": { | |
"scope": "powershell", | |
"prefix": "CFA07-create-ui-solution", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA06: Initialize a Dataverse solution project for the UI layer ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# The presentation (UI) layer refers to the user interface components like forms, views,", | |
"# pages, controls, scripts and command buttons which allow users to interact your app.", | |
"# Separating the database schema and backend business logic from the frontend provides", | |
"# flexibility in the solution architecture, easier change implementation and troubleshooting.", | |
"#", | |
"# In Dataverse, you can use the model-driven approach to easily create apps by generating", | |
"# forms and views for CRUD (Create, Read, Update, Delete) operations over tables. These", | |
"# frontend components are created within table definitions.", | |
"#", | |
"# The solution framework in Dataverse supports multiple solution layers contributing components", | |
"# to the same table (formerly called entities). By separating the UI into its own solution,", | |
"# you can uninstall it when necessary without losing data stored in the database.", | |
"#", | |
"# Default forms and views are created automatically with each table, but cannot be removed later.", | |
"# Therefore, it's better to define custom forms and views in a separate UI project.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Create a new solution project for the presentation (UI) layer using a template.", | |
"dotnet new pp-solution `", | |
"--output \"${1:src/Solutions.UI}\" `", | |
"--PublisherName \"${2:tomas}\" `", | |
"--PublisherPrefix \"${3:tom}\" `", | |
"--allow-scripts yes", | |
"", | |
"# Step 2: Add the existing tables initialized in the data model project to the UI solution.", | |
"dotnet new pp-entity `", | |
"--output \"${1:src/Solutions.UI}\" `", | |
"--Behavior \"Existing\" `", | |
"--PublisherPrefix \"${3:tom}\" `", | |
"--LogicalName \"${4:warehouseitem}\" `", | |
"--DisplayName \"${5:Warehouse Item}\" `", | |
"--SolutionRootPath \"${6:Declarations}\" `", | |
"--allow-scripts yes", | |
"", | |
"dotnet new pp-entity `", | |
"--output \"${1:src/Solutions.UI}\" `", | |
"--Behavior \"Existing\" `", | |
"--PublisherPrefix \"${3:tom}\" `", | |
"--LogicalName \"${7:warehousetransaction}\" `", | |
"--DisplayName \"${8:Warehouse Transaction}\" `", | |
"--SolutionRootPath \"${6:Declarations}\" `", | |
"--allow-scripts yes" | |
] | |
}, | |
"Create a Dataverse package and add the solutions to it": { | |
"scope": "powershell", | |
"prefix": "CFA08-create-pdpackage", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA07: Create a Dataverse Package and add solutions to it ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# A Package Deployer Tool (PDT) package (PDPackage) in Dataverse is an artifact that allows ", | |
"# you to bundle multiple Dataverse solutions, an ImportConfig.xml file, and custom .NET code ", | |
"# to handle various stages of solution import. The package is used with the Package Deployer ", | |
"# Tool to deploy multiple solutions with custom logic during deployment, such as data migrations, ", | |
"# validations or post-deployment operations.", | |
"#", | |
"# The ImportConfig.xml file defines the order in which solutions are imported. It can also", | |
"# contain how solution upgrade shoud be handled and execute import of configuration or test", | |
"# data packages exported from environments with Configuration Migration Tool (CMT).", | |
"#", | |
"# The ImportConfig.xml can be automatically generated from the ProjectReferences ", | |
"# of the PDPackage's .NET project using build targets. This ensures that the import process ", | |
"# respects the correct dependencies.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Create the Dataverse package for deployment.", | |
"pac package init --package-name ${1:Packages.Main} --outputDirectory \"src/${1:Packages.Main}\"", | |
"cd \"src/${1:Packages.Main}\"", | |
"", | |
"# Step 2: Add Dataverse solutions to the package as .NET ProjectReference items.", | |
"pac package add-reference --path ../Solutions.DataModel/", | |
"pac package add-reference --path ../Solutions.UI/", | |
"", | |
"# Step 3: Add the package project to the Visual Studio solution file.", | |
"dotnet sln ../../ add ${1:Packages.Main}.csproj", | |
"", | |
"# Return to the root directory.", | |
"cd ../.." | |
] | |
}, | |
"Build solution.zip files which can be imported": { | |
"scope": "powershell", | |
"prefix": "CFA09-build-solutions", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFA08: Build solution.zip files which can be imported ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# Dataverse solutions come in two forms: unmanaged and managed.", | |
"#", | |
"# Unmanaged solutions are for development and act as containers for tracking components.", | |
"# When deleted, components remain in the system. Use them to export definitions from your", | |
"# environment to source control.", | |
"#", | |
"# Managed solutions are for deployments. They layer component definitions, support merging", | |
"# of some components (diffs) and can be uninstalled if there are no dependencies. Import", | |
"# order is important. 'Managed properties' can restrict modifications to components.", | |
"#", | |
"# Production environments should not contain unmanaged components and unmanaged customizations", | |
"# should not be made in the Active layer.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Solution Building", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Import unmanaged solutions into developer environments for modifications with web tools. You", | |
"# need managed solutions to create layers and track diffs for syncing to source control.", | |
"#", | |
"# To build both types, set `<Managed>2</Managed>` in Solution.xml. The Solution Package Tool", | |
"# (SPT) can then pack both types from source. Microsoft.PowerApps.MSBuild.Solution NuGet", | |
"# triggers SPT when running the `Build` target (e.g., 'dotnet build').", | |
"#", | |
"# For PDPackage projects, use the `Publish` target to generate deployment packages.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Example 1: Build unmanaged solutions (default is Debug configuration).", | |
"dotnet build", | |
"", | |
"# Example 2: Build managed solutions (use Release configuration).", | |
"dotnet build --configuration Release", | |
"", | |
"# Example 3: Build the entire package with solutions included (works for unmanaged with Debug).", | |
"dotnet publish --configuration Release" | |
] | |
}, | |
"Commit the current changes to your local feature branch": { | |
"scope": "powershell", | |
"prefix": "CFB01-commit-initial-changes", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFB01: Commit the current changes to your local feature branch ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# In Git, a commit is a snapshot of the changes you've made to the code. It is a way to save", | |
"# your progress and document what changes were made. Commits are stored in your local branch", | |
"# and can be pushed to a remote repository for others to see.", | |
"#", | |
"# VS Code offers an easy way to see your Git changes. In the Source Control tab, you can view", | |
"# modified files, new files, and deleted files. The color indicators help you quickly identify", | |
"# which lines have been added, removed, or modified.", | |
"#", | |
"# After reviewing your changes, you need to 'stage' them (preparing them for a commit) using", | |
"# the 'git add' command. Once staged, you create a commit with a message describing the changes.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Add all changes to the staging area.", | |
"git add --all", | |
"", | |
"# Step 2: Commit the staged changes with a descriptive message.", | |
"git commit -m \"${1:Initialized repository, created UI and DataModel projects, created Main package}\"" | |
] | |
}, | |
"Create an Azure DevOps remote repository and push changes": { | |
"scope": "powershell", | |
"prefix": "CFB02-create-remote-repo", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFB02: Create an Azure DevOps remote repository and push changes ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# A remote repository is can be hosted on a platform like Azure DevOps or GitHub.", | |
"# It allows multiple developers to collaborate on the same project by pushing and pulling", | |
"# changes. When a developer pushes changes to the remote, others can pull those changes into ", | |
"# their local repositories, ensuring everyone is working with the latest code.", | |
"#", | |
"# Continuous Integration (CI) is an automated process where changes pushed to the remote ", | |
"# repository trigger builds and tests, ensuring that the codebase remains functional. ", | |
"# Integrating changes frequently helps avoid conflicts and errors in larger teams.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Set variables", | |
"\\$organization = \"${2:thenetworg}\"", | |
"\\$projectName = \"${3:DemoInventoryManagement}\"", | |
"\\$repositoryName = \"${4:DemoInventoryManagement}\"", | |
"", | |
"# Step 2: Log in to Azure CLI using your organizational account", | |
"az login --allow-no-subscriptions --tenant ${1:networg.com}", | |
"", | |
"# Step 3: Create a new Azure DevOps project with Git as the source control", | |
"az devops project create --name \\$projectName --source-control git --organization \"https://dev.azure.com/\\$organization\"", | |
"", | |
"# Step 4: Set Azure DevOps CLI defaults", | |
"az devops configure --defaults \"organization=https://dev.azure.com/\\$organization project=\\$projectName\"", | |
"", | |
"# Step 5: Connect the local Git repository to the new remote repository", | |
"git remote add origin \"https://\\$organization@dev.azure.com/\\$organization/\\$projectName/_git/\\$repositoryName\"", | |
"", | |
"# Step 6: Get the repository ID", | |
"\\$repositoryObject = az repos show --repository \\$repositoryName --project \"\\$projectName\" --output json", | |
"\\$repositoryId = (\\$repositoryObject | ConvertFrom-Json).id", | |
"", | |
"# Step 7: Push the current changes to the remote repository", | |
"git push -u origin --all", | |
"", | |
"# Step 8: Open the project in a browser", | |
"az devops project show --open --project \\$projectName" | |
] | |
}, | |
"Set up branch policy to prevent direct pushes to main": { | |
"scope": "powershell", | |
"prefix": "CFB03-setup-branch-policy", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFB03: Set up branch policy to prevent direct pushes to ${1:main} ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# A branch policy is essential for maintaining code quality and stability in a collaborative", | |
"# development environment. By preventing direct pushes to the ${1:main} branch, you ensure that all", | |
"# changes go through a review process via pull requests (PRs).", | |
"# This allows other team members to review, comment, and approve changes before they are merged ", | |
"# into the ${1:main} branch, reducing the risk of bugs or issues being introduced into production code.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Set up the branch policy to require at least one approver and prevent direct pushes to '${1:main}'.", | |
"az repos policy approver-count create `", | |
"--allow-downvotes true `", | |
"--blocking true `", | |
"--creator-vote-counts true `", | |
"--enabled true `", | |
"--minimum-approver-count 1 `", | |
"--reset-on-source-push true `", | |
"--branch \"${1:main}\" `", | |
"--repository-id \\$repositoryId `", | |
"--project \"\\$projectName\"", | |
"", | |
"# Step 2: Review the branch policy settings in the Azure DevOps project settings", | |
"Start-Process \"https://dev.azure.com/\\$organization/\\$projectName/_settings/repositories?_a=policiesMid&repo=\\$repositoryId&refs=refs/heads/${1:main}\"" | |
] | |
}, | |
"Create a new Power Platform developer environment": { | |
"scope": "powershell", | |
"prefix": "CFC01-create-dev-env", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFC01: Create a new Power Platform developer environment ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# Creating a Power Platform developer environment is significantly easier than traditional", | |
"# PaaS/IaaS setups. With a single command, all the required infrastructure (backend, frontend,", | |
"# database, networking, APIs and security) is ready in few moments.", | |
"#", | |
"# Any user can create up to 3 developer environments for free by assigning themselves the", | |
"# Power Apps Developer Plan (https://aka.ms/PowerApps/DeveloperPlan).", | |
"#", | |
"# In larger projects, you may set up automation to deploy daily CI builds into a queue", | |
"# of environments that developers can claim and use for their feature implementation.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Create a new developer environment (this may take some time).", | |
"pac admin create `", | |
"--name \"${1:Shift Planner Demo Environment}\" `", | |
"--currency ${2:EUR} `", | |
"--region ${3:europe} `", | |
"--type ${4:Developer} `", | |
"--domain ${5:inventorymgmtdev}" | |
] | |
}, | |
"Authenticate and connect to the development environment": { | |
"scope": "powershell", | |
"prefix": "CFC02-auth-connect-dev-env", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFC02: Authenticate and connect to the development environment ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# Before working with the development environment using the CLI, it's a good practice to", | |
"# clear any existing authentication profiles. This helps prevent accidental communication", | |
"# with the wrong environment and ensures that all subsequent commands are executed in the", | |
"# correct context.The following command clears the current authentication profile and ", | |
"# establishes a new connection to the environment we created earlier.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Clear any existing authentication profiles.", | |
"pac auth clear", | |
"", | |
"# Step 2: Authenticate and connect to the development environment", | |
"pac auth create --environment \"${1:https://inventorymgmtdev.crm4.dynamics.com/}\"", | |
"", | |
"# Step 3: Optionally add someone else to the environment to collaborate (not a good practice!)", | |
"pac admin assign-user `", | |
"--user \"${1:jan.hajek@REDACTED.com}\" `", | |
"--role \"System Administrator\"" | |
] | |
}, | |
"Import the solution to your Dataverse environment": { | |
"scope": "powershell", | |
"prefix": "CFC03-import-solution-to-dev-env", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFC03: Import the solution to your Dataverse environment ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# We are now ready to deploy the solutions to the Dataverse environment we created earlier.", | |
"# On Windows, you can use the package deployer to deploy solutions as a package.", | |
"# However, on macOS, the package deployer is not supported yet, so we need to manually import", | |
"# the solutions one by one.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Build the whole repository (as unmanaged solutions).", | |
"dotnet build", | |
"", | |
"# Step 2: Import the locally built solutions into Dataverse for modification in Power Apps Maker.", | |
"", | |
"# Windows:", | |
"pac package deploy --package \"./src/Packages.Main/bin/Debug/Packages.Main.1.0.0.pdpkg.zip\"", | |
"", | |
"# macOS:", | |
"cd \"./src/Solutions.DataModel/\"", | |
"pac solution import", | |
"cd ../..", | |
"", | |
"cd \"./src/Solutions.UI/\"", | |
"pac solution import", | |
"cd ../..", | |
"", | |
"# Step 3: Open the Power Apps Maker in the web browser.", | |
"pac tool maker" | |
] | |
}, | |
"Create database schema and a new Canvas app": { | |
"scope": "powershell", | |
"prefix": "CFD01-create-schema-and-canvas-app", | |
"body": [ | |
"# CFD01", | |
"# 1. open the empty App solution you imported", | |
"# 2. create a new Canvas app called 'Shift Planner' Canvas App", | |
"", | |
"# 3. add a data table to a screen with 'Shift Event' table as data source", | |
"# 4. include Subject, Actual Start and Actual End columns", | |
"# 5. add a button which will create new records using the following formula:", | |
"With(", | |
"\t{", | |
"\t\tStart: ", | |
"\t\t\tDateAdd(", | |
"\t\t\t\tDateAdd(", | |
"\t\t\t\t\tToday(),", | |
"\t\t\t\t\tRandBetween(0, 4) - Weekday(Today(), StartOfWeek.Monday) + 1,", | |
"\t\t\t\t\tTimeUnit.Days", | |
"\t\t\t\t),", | |
"\t\t\t\tRandBetween(9 * 60, 17 * 60 - 60),", | |
"\t\t\t\tTimeUnit.Minutes", | |
"\t\t\t)", | |
"\t},", | |
"\tPatch('Shift Events',", | |
"\tDefaults('Shift Events'),", | |
"\t{", | |
"\t\tSubject: Concatenate(\"Shift \", RandBetween(0, 30)),", | |
"\t\t'Actual Start': Start,", | |
"\t\t'Actual End': DateAdd(Start, 1, TimeUnit.Hours)", | |
"\t})", | |
");", | |
"", | |
"# 6. Generate few records" | |
] | |
}, | |
"Synchronize the changes from live environment back to your repository": { | |
"scope": "powershell", | |
"prefix": "CFD02-sync-changes-to-repo", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFD02: Synchronize the changes from live environment back to your repository ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# After making changes directly in your Dataverse environment, you can sync those changes", | |
"# back to your source control. This can be done either by exporting the solution to a separate", | |
"# folder or by using the CLI to sync the changes directly into your solution project.", | |
"#", | |
"# This must be done for individual solutions, as each solution is managed separately.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Publish all customizations in the environment", | |
"pac solution publish", | |
"", | |
"# Step 2: Sync changes from the UI solution", | |
"cd \"./src/Solutions.UI/\"", | |
"pac solution sync --packagetype Both --solution-folder Declarations --processCanvasApps", | |
"cd ../..", | |
"", | |
"# Step 3: Sync changes from the Data Model solution", | |
"cd \"./src/Solutions.DataModel/\"", | |
"pac solution sync --packagetype Both --solution-folder Declarations", | |
"cd ../.." | |
] | |
}, | |
"Export data and add them to the package": { | |
"scope": "powershell", | |
"prefix": "CFD03-export-data-and-add-to-package", | |
"body": [ | |
"# CFD03", | |
"# Export data from the live environment and add them to the package", | |
"mkdir \"src/Packages.Main/Data\"", | |
"cd \"src/Packages.Main\"", | |
"", | |
"pac tool cmt", | |
"", | |
"pac data export --schemaFile \"Data\\schema.xml\" --dataFile \"Data\\data.zip\"", | |
"", | |
"# Unzip data.zip", | |
"Expand-Archive \"Data\\data.zip\" -DestinationPath Data", | |
"", | |
"# Remove extra files", | |
"Remove-Item \"Data\\``[Content_Types``].xml\", \"Data\\data.zip\", \"Data\\schema.xml\" -ErrorAction SilentlyContinue", | |
"", | |
"# Add data.zip to ImportConfig.xml", | |
"", | |
"# Change data and ZIP it again (can be replaced with a build task)", | |
"Compress-Archive -Path \"Data\\*.xml\" -Update -DestinationPath \"Data\\data.zip\"", | |
"", | |
"# Import the data back (you can use the folder name without need for the zip file)", | |
"pac data import --data \"Data\"", | |
"", | |
"cd ../..", | |
"", | |
"", | |
"", | |
"# Alternatively you can use the a MSBuild target to automate the process during package build", | |
"# <Target Name=\"ZipDataPackage\" AfterTargets=\"CopyManagedSolutionsFromBuildToPackageOutput\" Condition=\"Exists('$(DefaultDataPackageFolder)')\">", | |
"# <MakeDir Directories=\"$(OutputPkgFolder)\" />", | |
"# <ZipDirectory SourceDirectory=\"$(DefaultDataPackageFolder)\" DestinationFile=\"$(OutputPkgFolder)DefaultDataPackage.zip\" Overwrite=\"true\" />", | |
"# </Target>" | |
] | |
}, | |
"Commit the changes": { | |
"scope": "powershell", | |
"prefix": "CFD04-commit-changes", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFD04: Commit the changes to your local repository ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# Step 1: Add all the changes to the staging area", | |
"git add --all", | |
"", | |
"# Step 2: Commit the changes with a descriptive message.", | |
"git commit -m \"${1:feat: created Shift Event table and Shift Planner Canvas app}\"", | |
"", | |
"# Step 3: Push the changes to the remote repository", | |
"git push" | |
] | |
}, | |
"Create a pull request to integrate changes into the main branch": { | |
"scope": "powershell", | |
"prefix": "CFD05-create-pull-request", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFD05: Create a pull request to integrate changes into the main branch ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# A pull request (PR) is a method for submitting contributions to a repository. Once you've", | |
"# committed your changes locally and pushed them to a branch in the remote repository, the", | |
"# next step is to create a pull request to propose merging your changes into the main branch.", | |
"#", | |
"# Pull requests allow for code review, discussion, and testing before the changes are", | |
"# integrated into the main branch. It ensures that the codebase stays clean and stable.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"", | |
"# Step 1: Create a pull request in Azure DevOps using the Azure CLI and open it in a browser", | |
"az repos pr create `", | |
"--project \"\\$projectName\" `", | |
"--repository \"\\$repositoryName\" `", | |
"--source-branch \"${3:users/tomas.prokop/project-setup}\" `", | |
"--target-branch \"${4:main}\" `", | |
"--title \"${5:feat: add Shift Event app and database}\" `", | |
"--description \"${6:Initialization of the app}\" `", | |
"--open", | |
"", | |
"# Step 2: Approve and merge the pull request in the Azure DevOps UI" | |
] | |
}, | |
"Sync main branch and create a new branch for pipeline files": { | |
"scope": "powershell", | |
"prefix": "CFE01-sync-main-create-pipeline-branch", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE01: Sync main branch and create a new branch for introducing pipeline files ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# After the pull request is merged, you need to sync your local main branch with the remote", | |
"# to ensure it's up to date. Then, you'll create a new branch to introduce pipeline files", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Checkout the main branch and pull the latest changes from the remote", | |
"git checkout main", | |
"git pull origin main", | |
"", | |
"# Step 2: Create a new branch for introducing pipeline files", | |
"git checkout -b \"${1:users/tomas.prokop/add-pipelines}\"" | |
] | |
}, | |
"Create pipeline source files": { | |
"scope": "powershell", | |
"prefix": "CFE02-create-pipeline-files", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE02: Create Azure DevOps pipeline source YAML files ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# We will create two Azure DevOps pipeline files. One for building artifacts from the source,", | |
"# and the second for deploying packages. These YAML definitions can be stored alongside your", | |
"# code in the repository, synced to the remote repository and then used to create pipelines", | |
"# using the Azure CLI.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Create a 'pipelines' directory to store pipeline YAML files", | |
"New-Item -ItemType Directory -Path \"pipelines\"", | |
"", | |
"# Step 2: Create files for the build and deployment pipelines", | |
"\"# type CFE03 and then press control+space to trigger suggestions of snippets\" | Out-File -FilePath \"pipelines/build.yml\"", | |
"\"# type CFE04 and then press control+space to trigger suggestions of snippets\" | Out-File -FilePath \"pipelines/deploy.yml\"", | |
"", | |
"# Step 3: Open the files in your editor to add the pipeline definitions (CFE03 and CFE04)", | |
"# Suggestions for conde snippets can be triggered by control + space", | |
"code \"pipelines/build.yml\"", | |
"code \"pipelines/deploy.yml\"" | |
] | |
}, | |
"Build pipeline template": { | |
"scope": "yaml", | |
"prefix": "CFE03-build-pipeline-template", | |
"body": [ | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE03: Template of a build pipeline template for building artifacts from source ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"", | |
"# Versioning the pipeline output with date and revision number", | |
"name: 0.0.1\\$(Date:yyMM).\\$(Rev:r)", | |
"", | |
"# This pipeline does not trigger automatically (triggered manually or by another pipeline)", | |
"trigger: none", | |
"", | |
"# Using the 'windows-latest' virtual machine image to run the pipeline", | |
"pool:", | |
"\tvmImage: 'windows-latest'", | |
"", | |
"# Defining the steps to be executed in this pipeline", | |
"steps:", | |
"", | |
"# Step 1: Use the .NET CLI to publish the project in Release configuration", | |
"- task: DotNetCoreCLI@2", | |
"\tinputs:", | |
"\t\tcommand: 'publish'", | |
"\t\tpublishWebProjects: false", | |
"\t\tprojects: 'src/Packages.Main/Packages.Main.csproj'", | |
"\t\targuments: '--configuration Release'", | |
"\t\tzipAfterPublish: false", | |
"", | |
"# Step 2: Copy the published .pdpkg.zip files to the staging directory", | |
"- task: CopyFiles@2", | |
"\tinputs:", | |
"\t\tSourceFolder: '\\$(Build.Repository.LocalPath)\\src\\'", | |
"\t\tContents: '**\\*.pdpkg.zip'", | |
"\t\tTargetFolder: '\\$(build.artifactstagingdirectory)\\Packages'", | |
"\t\tflattenFolders: true", | |
"", | |
"# Step 3: Publish the build artifacts (the .zip files) to the artifact staging directory", | |
"- task: PublishBuildArtifacts@1", | |
"\tinputs:", | |
"\t\tPathtoPublish: '\\$(Build.ArtifactStagingDirectory)'", | |
"\t\tArtifactName: 'drop'", | |
"\t\tpublishLocation: 'Container'", | |
"" | |
] | |
}, | |
"Deployment pipeline template": { | |
"scope": "yaml", | |
"prefix": "CFE04-deployment-pipeline-template", | |
"body": [ | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE04: Template of a deployment pipeline for deploying packages to environment ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"", | |
"# Versioning the pipeline output with date and revision number", | |
"name: 0.0.1\\$(Date:yyMM).\\$(Rev:r)", | |
"", | |
"# This pipeline does not trigger automatically (triggered manually or by another pipeline)", | |
"trigger: none", | |
"", | |
"# Using the 'windows-latest' virtual machine image to run the pipeline", | |
"pool:", | |
"\tvmImage: 'windows-latest'", | |
"", | |
"# Defining the steps to download artifacts and deploy the package", | |
"steps:", | |
"", | |
"# Step 1: Download the build artifacts from a specific build pipeline", | |
"- task: DownloadPipelineArtifact@2", | |
"\tinputs:", | |
"\t\tbuildType: 'specific'", | |
"\t\tproject: 'PROJECT_ID_PLACEHOLDER' # Update with your project ID", | |
"\t\tdefinition: 'BUILD_PIPELINE_ID_PLACEHOLDER' # Update with your build pipeline ID", | |
"\t\tbuildVersionToDownload: 'latestFromBranch'", | |
"\t\tbranchName: 'refs/heads/main'", | |
"\t\titemPattern: 'drop/Packages/**.pdpkg.zip'", | |
"\t\ttargetPath: '\\$(Pipeline.Workspace)'", | |
"", | |
"# Step 2: Install the Power Platform CLI tool if not already installed", | |
"- task: PowerPlatformToolInstaller@2", | |
"\tinputs:", | |
"\t\tDefaultVersion: true", | |
"", | |
"# Step 3: Deploy the package to the specified Dataverse environment", | |
"- task: PowerPlatformDeployPackage@2", | |
"\tinputs:", | |
"\t\tauthenticationType: 'PowerPlatformSPN'", | |
"\t\tPowerPlatformSPN: 'InventoryManagementServiceConnection' # Update with your service principal name", | |
"\t\tEnvironment: 'https://inventorymgmttest.crm4.dynamics.com' # Update with your environment URL", | |
"\t\tPackageFile: '\\$(Pipeline.Workspace)/drop/Packages/Packages.Main.1.0.0.pdpkg.zip'", | |
"" | |
] | |
}, | |
"Create a pull request to integrate pipelines into the main branch": { | |
"scope": "powershell", | |
"prefix": "CFE05-create-pipelines-pull-request", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE05: Create a pull request to integrate pipelines into the main branch ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# Step 1: Add all the changes to the staging area", | |
"git add --all", | |
"", | |
"# Step 2: Commit the changes with a descriptive message", | |
"git commit -m \"${1:chore: created build and deployment pipeline YAML files}\"", | |
"", | |
"# Step 3: Push the changes to the remote repository", | |
"git push -u origin -all", | |
"", | |
"# Step 4: Create a pull request in Azure DevOps using the Azure CLI and open it in a browser", | |
"az repos pr create `", | |
"--project \"\\$projectName\" `", | |
"--repository \"\\$repositoryName\" `", | |
"--source-branch \"${3:users/tomas.prokop/add-pipelines}\" `", | |
"--target-branch \"${4:main}\" `", | |
"--title \"${5:chore: add build and deployment pipelines}\" `", | |
"--description \"${6:Initialization pipelines}\" `", | |
"--open", | |
"", | |
"# Step 5: Approve and merge the pull request in the Azure DevOps UI", | |
"", | |
"# Step 6: Delete the branch locally and swith to the main branch and sync", | |
"git branch -d \"${3:users/tomas.prokop/add-pipelines}\"", | |
"git checkout main", | |
"git pull origin main" | |
] | |
}, | |
"Create pipelines in Azure DevOps from the files": { | |
"scope": "powershell", | |
"prefix": "CFE06-create-pipelines-ado", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE06: Create pipelines in Azure DevOps from the files ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# Once the pull request for the pipeline definitions has been merged into the main branch,", | |
"# the next step is to create the pipelines in Azure DevOps. Azure Pipelines will read the", | |
"# YAML definitions stored in your repository to configure build and deployment processes.", | |
"#", | |
"# You will use the Azure CLI to create these pipelines. The command references the YAML files", | |
"# in the repository and configures the pipelines without running them immediately (skipping", | |
"# the first run).", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Create the build pipeline.", | |
"\\$buildPipelineCreateOutput = az pipelines create `", | |
"--name \"${2:Build} Pipeline\" `", | |
"--project \"\\$projectName\" `", | |
"--repository \"\\$repositoryName\" `", | |
"--repository-type tfsgit `", | |
"--branch \"${5:main}\" `", | |
"--yaml-path \"pipelines/build.yml\" `", | |
"--skip-first-run", | |
"", | |
"\\$buildPipelineId = (\\$buildPipelineCreateOutput | ConvertFrom-Json).id", | |
"", | |
"# Step 2: Create the deployment pipeline.", | |
"az pipelines create `", | |
"--name \"${6:Deploy} Pipeline\" `", | |
"--project \"\\$projectName\" `", | |
"--repository \"\\$repositoryName\" `", | |
"--repository-type tfsgit `", | |
"--branch \"${5:main}\" `", | |
"--yaml-path \"pipelines/deploy.yml\" `", | |
"--skip-first-run" | |
] | |
}, | |
"Update project and build pipeline ID in the deploy pipeline YAML": { | |
"scope": "powershell", | |
"prefix": "CFE07-update-project-build-id", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE07: Update project and build pipeline ID in deploy pipeline ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# After the project and build pipeline were created, the pipeline YAML definitions should be", | |
"# updated with the corresponding project ID and build pipeline ID.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Create a new branch for updating the deploy pipeline", | |
"git checkout -b \"${1:users/tomas.prokop/update-deploy-pipeline}\"", | |
"", | |
"# Step 2: Replace placeholders in the deploy pipeline YAML file with the actual project and build pipeline IDs.", | |
"\\$deployPipelineFilePath = \"./pipelines/deploy.yml\"", | |
"", | |
"# Update the YAML file with the project ID", | |
"(Get-Content \\$deployPipelineFilePath) -replace 'PROJECT_ID_PLACEHOLDER', \\$projectId | Set-Content \\$deployPipelineFilePath", | |
"", | |
"# Update the YAML file with the build pipeline ID", | |
"(Get-Content \\$deployPipelineFilePath) -replace 'BUILD_PIPELINE_ID_PLACEHOLDER', \\$buildPipelineId | Set-Content \\$deployPipelineFilePath", | |
"", | |
"# Step 3: Commit and push the changes", | |
"git add --all", | |
"git commit -m \"chore: updated project and build pipeline IDs in deploy pipeline\"", | |
"git push -u origin \"users/tomas.prokop/update-deploy-pipeline\"", | |
"", | |
"# Step 4: Create a pull request to integrate the changes into the main branch", | |
"az repos pr create `", | |
"--project \"\\$projectName\" `", | |
"--repository \"\\$repositoryName\" `", | |
"--source-branch \"${1:users/tomas.prokop/update-deploy-pipeline}\" `", | |
"--target-branch \"${2:main}\" `", | |
"--title \"${3:chore: update project and build pipeline IDs in deploy pipeline}\" `", | |
"--description \"${4:Update the project and build pipeline IDs in the deploy pipeline YAML file.}\"`", | |
"--open", | |
"", | |
"# Step 5: Approve and merge the pull request in the Azure DevOps UI", | |
"", | |
"# Step 6: Delete the branch locally and switch to the main branch", | |
"git branch -d \"${1:users/tomas.prokop/update-deploy-pipeline}\"", | |
"git checkout main", | |
"git pull origin main" | |
] | |
}, | |
"Create a test environment": { | |
"scope": "powershell", | |
"prefix": "CFE08-create-test-environment", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE08: Create a test environment ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# A test environment (UAT) is used for testing your solutions before they are deployed ", | |
"# to production.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Create a new test environment.", | |
"pac admin create `", | |
"--name \"${1:Shift Planner Test Environment}\" `", | |
"--currency ${2:EUR} `", | |
"--region ${3:europe} `", | |
"--type ${4:Sandbox} `", | |
"--domain ${5:inventorymgmttest}", | |
"", | |
"# Step 2: Authenticate and connect to the test environment.", | |
"pac auth clear", | |
"pac auth create --environment \"https://inventorymgmttest.crm4.dynamics.com/\"" | |
] | |
}, | |
"Create app registration and service connection in Azure DevOps": { | |
"scope": "powershell", | |
"prefix": "CFE09-create-service-connection", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE09: Create app registration and service connection in Azure DevOps ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# Workload identity federation allows Azure DevOps to authenticate against Azure services", | |
"# without the need for long-lived credentials. A trust is established between Azure DevOps", | |
"# and an Azure service principal and Azure DevOps provides short-lived tokens for Azure API.", | |
"#", | |
"# This snippet creates a service principal and configures a service connection in Azure DevOps", | |
"# that the pipeline tasks can use to authenticate against Dataverse.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Set variables", | |
"\\$projectInfo = az devops project show --project \"\\$projectName\" --query \"{id: id, name: name}\" --output json | ConvertFrom-Json", | |
"\\$projectId = \\$projectInfo.id", | |
"\\$projectName = \\$projectInfo.name", | |
"\\$tenantId = az account show --query \"tenantId\" --output tsv", | |
"", | |
"# Step 2: Create a service principal for Dataverse.", | |
"\\$pacServicePrincipalOutput = pac admin create-service-principal", | |
"\\$appRegistrationId = (\\$pacServicePrincipalOutput -split \"\n\" | Where-Object {\\$_ -match \"Application Id\"}) -replace \"Application Id\\s+\", \"\"", | |
"Write-Host \"Application Id: \" -ForegroundColor Yellow", | |
"Write-Host \\$appRegistrationId -ForegroundColor Green", | |
"", | |
"# Step 3: Set up workload identity federation in Entra ID.", | |
"\\$federatedCredentialObject = @\"", | |
"{", | |
" \"name\": \"ADOTest\",", | |
" \"issuer\": \"https://vstoken.dev.azure.com/2efd1716-20ec-43ed-a8a0-3eb01a9e236e\",", | |
" \"subject\": \"sc://thenetworg/DemoInventoryManagement/InventoryManagementServiceConnection\",", | |
" \"description\": \"Azure DevOps Service Connection\",", | |
" \"audiences\": [", | |
" \"api://AzureADTokenExchange\"", | |
" ]", | |
"}", | |
"\"@", | |
"", | |
"az ad app federated-credential create --id \\$appRegistrationId --parameters \"\\$federatedCredentialObject\"", | |
"", | |
"# Step 4: Open the application in the browser to manage credentials.", | |
"\\$appRegistrationUrl = \"https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Credentials/appId/\\$appRegistrationId/isMSAApp~/false\"", | |
"Start-Process \\$appRegistrationUrl", | |
"", | |
"# Step 5: Create a service connection in Azure DevOps", | |
"\\$serviceEndpointTempFilePath = \"./serviceConnectionTemp.json\"", | |
"\\$serviceEndpointObject = @\"", | |
"{", | |
" \"authorization\": {", | |
" \"parameters\": {", | |
" \"serviceprincipalid\": \"\\$appRegistrationId\",", | |
" \"tenantid\": \"\\$tenantId\"", | |
" },", | |
" \"scheme\": \"WorkloadIdentityFederation\"", | |
" },", | |
" \"name\": \"InventoryManagementServiceConnection\",", | |
" \"owner\": \"library\",", | |
" \"type\": \"powerplatform-spn\",", | |
" \"url\": \"https://inventorymgmttest.crm4.dynamics.com\",", | |
" \"serviceEndpointProjectReferences\": [", | |
" {", | |
" \"name\": \"InventoryManagementServiceConnection\",", | |
" \"projectReference\": {", | |
" \"id\": \"\\$projectId\",", | |
" \"name\": \"\\$projectName\"", | |
" }", | |
" }", | |
" ]", | |
"}", | |
"\"@", | |
"", | |
"\\$serviceEndpointObject | Out-File -FilePath \\$serviceEndpointTempFilePath -Encoding utf8", | |
"az devops service-endpoint create --service-endpoint-configuration \\$serviceEndpointTempFilePath --project \"\\$projectName\"", | |
"Remove-Item \\$serviceEndpointTempFilePath", | |
"", | |
"# Step 6: Grant access to all pipelines for the service connection", | |
"\\$serviceEndpointId = az devops service-endpoint list --query \"[?name=='InventoryManagementServiceConnection'].id\" -o tsv --project \"\\$projectName\"", | |
"az devops service-endpoint update --id \\$serviceEndpointId --enable-for-all --project \"\\$projectName\"", | |
"", | |
"# Step 7: Open the Azure DevOps project in the browser", | |
"az devops project show --project \"\\$projectName\" --open" | |
] | |
}, | |
"Run and open pipelines in the browser": { | |
"scope": "powershell", | |
"prefix": "CFE10-run-and-open-pipelines", | |
"body": [ | |
"#", | |
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗", | |
"# ║ CFE10: Run and open pipelines in the browser ║", | |
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝", | |
"#", | |
"# After creating your pipelines in Azure DevOps, you can run them using the Azure CLI", | |
"# and monitor their status by opening the pipeline pages in the browser.", | |
"#", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Commands", | |
"# ──────────────────────────────────────────────────────────────────────────────────────────", | |
"# Step 1: Run the build pipeline", | |
"az pipelines run --name \"Build Pipeline\" --project \"\\$projectName\"", | |
"", | |
"# Step 2: Open the build pipeline in the browser", | |
"az pipelines show --name \"Build Pipeline\" --project \"\\$projectName\" --open", | |
"", | |
"# Step 3: Run the deployment pipeline (wait for the build pipeline to finish first!)", | |
"az pipelines run --project \"\\$projectName\" --name \"Deploy Pipeline\"", | |
"", | |
"# Step 4: Open the deployment pipeline in the browser", | |
"az pipelines show --project \"\\$projectName\" --name \"Deploy Pipeline\" --open", | |
"", | |
"# Step 5: Open the Power Apps Maker in the web browser and check if the solutions were deployed", | |
"pac tool maker" | |
] | |
}, | |
"Initialize a new PCF control project": { | |
"scope": "powershell", | |
"prefix": "CFF01-init-pcf-control-project", | |
"body": [ | |
"# CFF01", | |
"# Initialize a new PCF control project", | |
"pac pcf init `", | |
"--namespace tom `", | |
"--name Calendar `", | |
"--template field `", | |
"--framework react `", | |
"--run-npm-install `", | |
"--outputDirectory \"src/Controls.Calendar\"", | |
"", | |
"cd \"src/Controls.Calendar\"", | |
"# Add the missing project type ID to the .pcfproj to make dotnet accept the custom project type", | |
"\\$csproj = Get-ChildItem -Path . -Filter *.pcfproj | Select-Object -First 1; [xml]\\$xml = Get-Content \\$csproj.FullName -Raw; \\$propertyGroup = \\$xml.Project.PropertyGroup | Select-Object -First 1; \\$newElement = \\$xml.CreateElement('DefaultProjectTypeGuid', \\$xml.Project.NamespaceURI); \\$newElement.InnerText = 'FAE04EC0-301F-11D3-BF4B-00C04F79EFBC'; \\$propertyGroup.AppendChild(\\$newElement) > \\$null; \\$xml.Save(\\$csproj.FullName)", | |
"", | |
"cd ../..", | |
"dotnet sln add \"src/Controls.Calendar/Controls.Calendar.pcfproj\"" | |
] | |
}, | |
"Initialize a new solution for PCF controls and add the control to it": { | |
"scope": "powershell", | |
"prefix": "CFF02-init-pcf-solution-project", | |
"body": [ | |
"# CFF02", | |
"# Initialize a new solution for PCF controls", | |
"pac solution init --publisher-name tomas --publisher-prefix tom --outputDirectory \"Solutions.Controls\"", | |
"cd \"Solutions.Controls\"", | |
"", | |
"${1:CFA06-rename-src-folder-remove-gitignore}", | |
"", | |
"${2:CFA07-patch-csproj}", | |
"", | |
"${3:CFA08-switch-type-sanitize-solution-name}", | |
"", | |
"# Add the project to the previously created Visual Studio solution", | |
"dotnet sln ../../ add Solutions.Controls.cdsproj", | |
"", | |
"# Add the control to the solution", | |
"pac solution add-reference --path ../Controls.Calendar", | |
"# Add the new Controls solution to the package", | |
"cd ..", | |
"cd Packages.Main", | |
"pac package add-reference --path \"../Solutions.Controls/\"", | |
"", | |
"cd ../.." | |
] | |
}, | |
"Upgrade React and install React Big Calendar": { | |
"scope": "powershell", | |
"prefix": "CFF03-upgrade-react-install-calendar", | |
"body": [ | |
"# CFF03", | |
"# Upgrade React and install React Big Calendar", | |
"cd \"src/Controls.Calendar\"", | |
"npm install react@17", | |
"npm install react-dom@17", | |
"npm install @types/react@17", | |
"npm install @types/react-dom@17", | |
"npm install react-big-calendar", | |
"npm install moment", | |
"npm i --save-dev @types/react-big-calendar", | |
"", | |
"# Enable esModuleInterop in tsconfig.json", | |
"# tsconfig.json > compilerOptions > \"esModuleInterop\": true", | |
"", | |
"cd ../.." | |
] | |
}, | |
"Calendar React component": { | |
"scope": "typescript,typescriptreact", | |
"prefix": "CFF04-calendar-react-component", | |
"body": [ | |
"// CFF04", | |
"import * as React from 'react';", | |
"import { Calendar, momentLocalizer, Views } from \"react-big-calendar\";", | |
"import \"react-big-calendar/lib/css/react-big-calendar.css\";", | |
"import moment from 'moment';", | |
"", | |
"export interface ICalendarComponentProps {", | |
"\tname?: string;", | |
"}", | |
"", | |
"const localizer = momentLocalizer(moment);", | |
"", | |
"// Sample events data for the current week", | |
"const events = [", | |
"\t{", | |
"\t\tid: 0,", | |
"\t\ttitle: 'Team Meeting',", | |
"\t\tstart: new Date(new Date().setHours(new Date().getHours() + 1)),", | |
"\t\tend: new Date(new Date().setHours(new Date().getHours() + 2))", | |
"\t},", | |
"\t{", | |
"\t\tid: 1,", | |
"\t\ttitle: 'Project Review',", | |
"\t\tstart: new Date(new Date().setDate(new Date().getDate() + 1)),", | |
"\t\tend: new Date(new Date().setDate(new Date().getDate() + 1)).setHours(new Date().getHours() + 1)", | |
"\t},", | |
"\t{", | |
"\t\tid: 2,", | |
"\t\ttitle: 'Client Call',", | |
"\t\tstart: new Date(new Date().setDate(new Date().getDate() + 2)),", | |
"\t\tend: new Date(new Date().setDate(new Date().getDate() + 2)).setHours(new Date().getHours() + 1)", | |
"\t}", | |
"];", | |
"", | |
"export class CalendarComponent extends React.Component<ICalendarComponentProps> {", | |
"\tpublic render(): React.ReactNode {", | |
"\t\treturn (", | |
"\t\t\t<Calendar", | |
"\t\t\tlocalizer={localizer}", | |
"\t\t\tevents={events}", | |
"\t\t\tstartAccessor=\"start\"", | |
"\t\t\tendAccessor=\"end\"", | |
"\t\t\tstyle={{ height: \"100%\" }}", | |
"\t\t\tdefaultView={Views.WEEK}", | |
"\t\t/>", | |
"\t\t)", | |
"\t}", | |
"}" | |
] | |
}, | |
"Import the Controls solution to your Dataverse dev environment": { | |
"scope": "powershell", | |
"prefix": "CFF06-import-solution-to-dev-env", | |
"body": [ | |
"# CFF06", | |
"# Build the whole repository", | |
"dotnet build", | |
"", | |
"# Import the locally built solutions to Dataverse so we can make modifications in the Power Apps Maker", | |
"cd \"./src/Solutions.Controls/\"", | |
"pac solution import", | |
"cd ../.." | |
] | |
}, | |
"Bump the PCF version, push the control to the environment and publish it": { | |
"scope": "powershell", | |
"prefix": "CFF06-push-pcf", | |
"body": [ | |
"# CFF06", | |
"# Bump the PCF version, push the control to the environment and publish it", | |
"pac pcf version --patchversion ${1:1}", | |
"pac pcf push --publisher-prefix ${2:tom}", | |
"pac solution publish" | |
] | |
}, | |
"Delete the environment and repository": { | |
"scope": "powershell", | |
"prefix": "CFZ99-delete-environment", | |
"body": [ | |
"pac admin delete --environment \"https://inventorymgmtdev.crm4.dynamics.com/\"", | |
"pac admin delete --environment \"https://inventorymgmttest.crm4.dynamics.com/\"", | |
"", | |
"az repos delete --id \\$repositoryId --yes --project \"\\$projectName\"", | |
"az devops project delete --yes --id \\$projectId", | |
"az ad app delete --id \\$appRegistrationId" | |
] | |
}, | |
"Create a form, create a onload script": { | |
"scope": "powershell", | |
"prefix": "CFZ98-create-form-script", | |
"body": [ | |
"# CFZ98", | |
"# Create a form, create a onload script", | |
"cd Solutions.UI/Declarations/Entities/tom_xyz/FormXml", | |
"dotnet new pp-form --EntityName \"\" --DisplayName \"Solutions.UI\" --PublisherName \"tomas\" --PublisherPrefix \"tom\" --allow-scripts yes", | |
"", | |
"dotnet new pp-script-library", | |
"", | |
"# Implement the onload function", | |
"# Register an event handler in the form" | |
] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment