Skip to content

Instantly share code, notes, and snippets.

@TomProkop
Last active September 28, 2024 16:05
Show Gist options
  • Save TomProkop/607a9de00d811a5ae68327e90f6a81cf to your computer and use it in GitHub Desktop.
Save TomProkop/607a9de00d811a5ae68327e90f6a81cf to your computer and use it in GitHub Desktop.
Code-first low-code demo VS Code workspace snippets
// 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. Perform steps from CFA00 below to setup your machine.
//
// 2. Install PowerShell VS Code extension:
// Open extension page from https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell
//
// 3. Create a working folder where the repository will be initialized and open it in VS Code:
// mkdir CodeFirstPowerPlatformDemo
// code CodeFirstPowerPlatformDemo
//
// 4. Open Terminal in VS Code and download the snippet file to your working folder:
// Invoke-WebRequest -Uri 'https://gist.githubusercontent.com/TomProkop/607a9de00d811a5ae68327e90f6a81cf/raw' -OutFile '.vscode/CodeFirstPowerPlatformDemo.code-snippets'
//
// 5. Create a PowerShell script file where you'll be executing the commands from this demo:
// New-Item -ItemType File -Name '.demo/DemoScriptPad.ps1'
//
// 6. Make the current 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
//
// 7. 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. 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}\"",
"",
"# 5. 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\"",
"",
"# 6. Install Azure CLI. More info: aka.ms/azcli-docs",
"# Windows:",
"winget install -e --id \"Microsoft.AzureCLI\"",
"# macOS:",
"brew install azure-cli",
"",
"# 7. Install Azure DevOps CLI extension",
"az extension add --name \"azure-devops\"",
"",
"# 8. 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:ShiftPlanner}",
"",
"# 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 Dataverse solution project for a database schema": {
"scope": "powershell",
"prefix": "CFA03-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 a new Activity table in Dataverse model solution": {
"scope": "powershell",
"prefix": "CFA04-create-table",
"body": [
"#",
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗",
"# ║ CFA04: Create a new Activity table in Dataverse model solution ║",
"# ╚════════════════════════════════════════════════════════════════════════════════════════╝",
"#",
"# We will create a new table in the Dataverse solution for tracking events.",
"# Dataverse supports multiple table types and in this case, we are adding an Activity table.",
"# Activity tables in Dataverse have some predefined columns (such as subject, date from/to),",
"# which support a unified view across different types of activities.",
"#",
"# 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 the new Activity table with predefined columns to the solution.",
"dotnet new pp-entity `",
"--output \"src/${1:Solutions.DataModel}\" `",
"--EntityType \"${2:Activity}\" `",
"--Behavior \"${3:New}\" `",
"--PublisherPrefix \"${4:tom}\" `",
"--LogicalName \"${5:shiftevent}\" `",
"--LogicalNamePlural \"${6:shiftevents}\" `",
"--DisplayName \"${7:Shift Event}\" `",
"--DisplayNamePlural \"${8:Shift Events}\" `",
"--SolutionRootPath \"${9:Declarations}\" `",
"--allow-scripts yes"
]
},
"Initialize a Dataverse solution project for a UI layer": {
"scope": "powershell",
"prefix": "CFA05-create-ui-solution",
"body": [
"#",
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗",
"# ║ CFA05: 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 table (${5:Shift Event}) 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:shiftevent}\" `",
"--DisplayName \"${5:Shift Event}\" `",
"--SolutionRootPath \"${6:Declarations}\" `",
"--allow-scripts yes"
]
},
"Create a Dataverse package and add the solutions to it": {
"scope": "powershell",
"prefix": "CFA06-create-pdpackage",
"body": [
"#",
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗",
"# ║ CFA06: 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": "CFA07-build-solutions",
"body": [
"#",
"# ╔════════════════════════════════════════════════════════════════════════════════════════╗",
"# ║ CFA07: 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:DemoShiftPlanner}\"",
"\\$repositoryName = \"${4:DemoShiftPlanner}\"",
"",
"# 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:shiftplannerdev}"
]
},
"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://shiftplannerdev.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: 'ShiftPlannerServiceConnection' # Update with your service principal name",
"\t\tEnvironment: 'https://shiftplannertest.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:shiftplannertest}",
"",
"# Step 2: Authenticate and connect to the test environment.",
"pac auth clear",
"pac auth create --environment \"https://shiftplannertest.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/DemoShiftPlanner/ShiftPlannerServiceConnection\",",
" \"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\": \"ShiftPlannerServiceConnection\",",
" \"owner\": \"library\",",
" \"type\": \"powerplatform-spn\",",
" \"url\": \"https://shiftplannertest.crm4.dynamics.com\",",
" \"serviceEndpointProjectReferences\": [",
" {",
" \"name\": \"ShiftPlannerServiceConnection\",",
" \"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=='ShiftPlannerServiceConnection'].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://shiftplannerdev.crm4.dynamics.com/\"",
"pac admin delete --environment \"https://shiftplannertest.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