Skip to content

Commit 55366b0

Browse files
mawasilemattdot
andauthored
Adding support for billing policy (#214)
* fix: update README to include Azure CLI installation instructions and correct typos * feat: enable use_cli for Power Platform provider as default login type * fix: update default value of RunSolutionChecker parameter to false * feat: add Power Platform billing policy configuration and related variables * fix: align formatting of power_platform_billing_policy variable definition * fix: update dependency groups in dependabot configuration * fix: update README and Terraform files for billing policy and storage container access * fix: align formatting of billing policy name variable * Update azd-hooks/scripts/hooks/postprovision/deploy_power_platform_solution.ps1 Co-authored-by: Matt Dotson <[email protected]> * feat: consolidate billing policy resources into power_platform_core.tf * fix: update environment variable checks in preprovision script for RS_CONTAINER_NAME * fix: remove unnecessary newlines in provider.tf * fix: format code for consistency in power_platform_core.tf --------- Co-authored-by: Matt Dotson <[email protected]>
1 parent 14ff5f2 commit 55366b0

File tree

10 files changed

+108
-33
lines changed

10 files changed

+108
-33
lines changed

.github/dependabot.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ updates:
77
commit-message:
88
prefix: "chore(deps):"
99
groups:
10-
all-dependencies:
10+
devcontainer:
1111
patterns:
1212
- "*"
1313
- package-ecosystem: "terraform"
@@ -19,7 +19,7 @@ updates:
1919
commit-message:
2020
prefix: "chore(deps):"
2121
groups:
22-
all-dependencies:
22+
terraform-providers:
2323
patterns:
2424
- "*"
2525
- package-ecosystem: "github-actions"
@@ -29,6 +29,6 @@ updates:
2929
commit-message:
3030
prefix: "chore(deps):"
3131
groups:
32-
all-dependencies:
32+
github-actions:
3333
patterns:
3434
- "*"

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,17 @@ This architecture ensures that sensitive enterprise data never traverses public
7575
- [**Copilot in Power Apps**](https://learn.microsoft.com/en-us/power-apps/maker/canvas-apps/ai-overview?WT.mc_id=ppac_inproduct_settings): Enable this setting to allow AI-powered assistance within Power Apps development
7676
- [**Publish Copilots with AI features**](https://learn.microsoft.com/en-us/microsoft-copilot-studio/security-and-governance): Allow Copilot authors to publish from Copilot Studio when AI features are enabled
7777
- **Power Platform licenses**. The designated user must have the following Power Platform licenses assigned:
78-
- Microsoft Power Apps
79-
- Power Automate
80-
- Copilot Studio
78+
- **Microsoft Power Apps**
79+
- **Power Automate**
80+
- **Copilot Studio**
81+
82+
To simplify license management, you can use an Azure subscription with a Billing Policy instead of assigning licenses directly. Configure this by using the following flag:
83+
84+
```shell
85+
azd env set USE_BILLING_POLICY "true"
86+
```
87+
88+
**Note:** After creating the Billing Policy, navigate to the [Power Platform Admin Center](https://aka.ms/ppac) and ensure that the *Copilot Studio* product is selected. This is a known issue that will be addressed in future updates.
8189

8290
### User Configuration
8391

@@ -125,6 +133,7 @@ A related option is VS Code Dev Containers, which will open the project in your
125133
126134
1. Install the required tools:
127135
136+
- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-windows?view=azure-cli-latest&pivots=winget) - Required for managing Azure resources and authentication
128137
- [Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd) - Platform-specific installers available via package managers or direct download
129138
- [PowerShell 7](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.5) - Required for non-Windows systems; Windows users may use built-in PowerShell
130139
- [.NET 8.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) - Includes .NET CLI, runtime, and development tools
@@ -149,7 +158,7 @@ A related option is VS Code Dev Containers, which will open the project in your
149158
150159
The steps below will provision Azure and Power Platform resources and will deploy Copilot Studio bot.
151160
152-
1. Login to you Azure and config azd to use Az CLI authentication:
161+
1. Login to your Azure account and config azd to use Az CLI authentication:
153162
154163
```shell
155164
az login --service-principal --username <SP_CLIENT_ID> --password <SP_SECRET> --tenant <TENANT_ID>
@@ -160,7 +169,6 @@ The steps below will provision Azure and Power Platform resources and will deplo
160169
161170
```shell
162171
pac auth create --name az-cli-auth --applicationId <SP_CLIENT_ID> --clientSecret <SP_SECRET> --tenant <TENANT_ID> --accept-cleartext-caching
163-
export POWER_PLATFORM_USE_CLI="true"
164172
```
165173
166174
*Note: the `pac auth create` command may return a warning about being unable to connect to a Dataverse organization. This is expected, and will not impact the deployment.*

azd-hooks/scripts/hooks/postprovision/deploy_power_platform_solution.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
ID of the Power Platform environment to deploy to
2727
2828
.PARAMETER RunSolutionChecker
29-
Whether to run solution checker after deployment (default: true)
29+
Whether to run solution checker after deployment (default: false)
3030
3131
.PARAMETER AISearchConnectionId
3232
Direct connection ID for the Azure AI Search connector (highest priority)

azd-hooks/scripts/hooks/preprovision/run_preconfig.ps1

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
<#
2+
.SYNOPSIS
3+
Checks the existence of the RS_CONTAINER_NAME environment variable.
4+
5+
.DESCRIPTION
6+
This script checks if the RS_CONTAINER_NAME environment variable is set.
7+
The presence or absence of this variable is used to determine whether
8+
local storage should be used or not.
9+
10+
#>
111
$script:infraDir = "infra"
212
$script:providerConfPath = Join-Path $script:infraDir "provider.conf.json"
313
$script:providerTfPath = Join-Path $script:infraDir "provider.tf"
@@ -130,31 +140,30 @@ terraform {
130140
}
131141
}
132142

133-
Write-Host "Checking if USE_LOCAL_STATE environment variable exists..."
134143

135-
# Check if USE_LOCAL_STATE azd environment variable exists
144+
Write-Host "Checking if RS_CONTAINER_NAME environment variable exists..."
145+
# Check if RS_CONTAINER_NAME azd environment variable exists
136146
try {
137147

138-
$useLocalState = azd env get-value USE_LOCAL_STATE 2>$null
139-
if ($LASTEXITCODE -eq 0 -and $useLocalState) {
140-
Write-Host "USE_LOCAL_STATE environment variable exists with value: $useLocalState"
148+
$rsContainerName = azd env get-value RS_CONTAINER_NAME 2>$null
149+
if ($LASTEXITCODE -eq 0 -and $rsContainerName) {
150+
Write-Host "RS_CONTAINER_NAME environment variable exists with value: $rsContainerName"
141151

142-
# Check if the value is set to true (case insensitive)
143-
if ($useLocalState.ToLower() -eq "true") {
144-
Write-Host "✓ Local state is enabled"
145-
Initialize-LocalStorage
146-
} else {
147-
Write-Host "ℹ Local state is disabled (value: $useLocalState)"
152+
# Check if the value is not empty or null
153+
if (-not [string]::IsNullOrWhiteSpace($rsContainerName)) {
154+
Write-Host "✓ RS_CONTAINER_NAME is set to a valid value"
148155
Initialize-RemoteStorage
156+
} else {
157+
Write-Host "ℹ RS_CONTAINER_NAME is empty or null"
158+
Initialize-LocalStorage
149159
}
150160
} else {
151-
Write-Host "✗ USE_LOCAL_STATE environment variable does not exist or is empty"
152-
azd env set USE_LOCAL_STATE false
153-
Initialize-RemoteStorage
154-
161+
Write-Host "✗ RS_CONTAINER_NAME environment variable does not exist or is empty"
162+
azd env set RS_CONTAINER_NAME ""
163+
Initialize-LocalStorage
155164
}
156165
} catch {
157-
Write-Host "✗ Error checking USE_LOCAL_STATE environment variable: $($_.Exception.Message)"
166+
Write-Host "✗ Error checking RS_CONTAINER_NAME environment variable: $($_.Exception.Message)"
158167
Write-Host "ℹ Make sure you're in an azd environment directory"
159168
exit 1
160169
}

infra/main.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ module "copilot_studio" {
4343
# and the module will attempt to manage the existing environment instead.
4444
power_platform_environment = var.power_platform_environment
4545
power_platform_managed_environment = var.power_platform_managed_environment
46+
47+
# Power Platform billing policy configuration
48+
# Controls whether the module should create a new billing policy or use an existing one
49+
power_platform_billing_policy = {
50+
name = var.power_platform_billing_policy.name
51+
should_create = var.use_billing_policy
52+
}
53+
subscription_id = data.azurerm_client_config.current.subscription_id
4654
}
4755

4856
# Example tenant settings to support generative answers in Copilot.

infra/main.tfvars.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"location": "${AZURE_LOCATION}",
3+
"use_billing_policy": "${USE_BILLING_POLICY:-false}",
34
"azd_environment_name": "${AZURE_ENV_NAME}",
45
"resource_share_user": ${RESOURCE_SHARE_USER},
56
"deploy_github_runner": "${DEPLOY_GITHUB_RUNNER:-false}",

infra/modules/copilot_studio/power_platform_core.tf

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,25 @@ locals {
66
power_platform_environment_location = coalesce(var.power_platform_environment.location, powerplatform_environment.this[0].location)
77
}
88

9+
resource "powerplatform_billing_policy" "this" {
10+
count = var.power_platform_billing_policy.should_create && var.power_platform_environment.id == "" ? 1 : 0
11+
12+
name = "${var.power_platform_billing_policy.name}${var.unique_id}"
13+
location = var.power_platform_environment.location
14+
status = "Enabled"
15+
billing_instrument = {
16+
resource_group = var.resource_group_name
17+
subscription_id = var.subscription_id
18+
}
19+
}
20+
921
resource "powerplatform_environment" "this" {
10-
count = var.power_platform_environment.id == "" ? 1 : 0
11-
location = var.power_platform_environment.location
12-
display_name = "${var.power_platform_environment.name} - ${var.unique_id}"
13-
environment_type = var.power_platform_environment.environment_type
22+
count = var.power_platform_environment.id == "" ? 1 : 0
23+
24+
billing_policy_id = var.power_platform_billing_policy.should_create ? powerplatform_billing_policy.this[0].id : null
25+
location = var.power_platform_environment.location
26+
display_name = "${var.power_platform_environment.name} - ${var.unique_id}"
27+
environment_type = var.power_platform_environment.environment_type
1428
dataverse = {
1529
language_code = var.power_platform_environment.language_code
1630
currency_code = var.power_platform_environment.currency_code

infra/modules/copilot_studio/variables.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@ variable "failover_vnet_name" {
99
}
1010

1111

12+
variable "power_platform_billing_policy" {
13+
type = object({
14+
should_create = optional(bool, false)
15+
name = string
16+
})
17+
description = <<DESCRIPTION
18+
- `name`: The name of the Power Platform billing policy.
19+
- `should_create`: If set to false, the billing policy will not be created. Defaults to false.
20+
DESCRIPTION
21+
}
22+
23+
variable "subscription_id" {
24+
description = "The Azure subscription ID to use for billing."
25+
type = string
26+
}
1227

1328
variable "power_platform_environment" {
1429
type = object({

infra/provider.tf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ provider "azapi" {
7272
data "azurerm_client_config" "current" {}
7373

7474
# Configure Power Platform provider
75-
provider "powerplatform" {}
76-
77-
78-
75+
provider "powerplatform" {
76+
# PowerPlatform provider will use the same credentials as Azure provider by default
77+
use_cli = true
78+
}

infra/variables.tf

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,26 @@ variable "location" {
224224
nullable = false
225225
}
226226

227+
variable "use_billing_policy" {
228+
type = bool
229+
default = false
230+
description = "If true, the billing policy will be created. If false, the billing policy will not be created."
231+
}
232+
233+
variable "power_platform_billing_policy" {
234+
type = object({
235+
should_create = optional(bool, false)
236+
name = string
237+
})
238+
default = {
239+
name = "copilotStudioBillingPolicy"
240+
}
241+
description = <<DESCRIPTION
242+
- `name`: The name of the Power Platform billing policy.
243+
- `should_create`: If set to false, the billing policy will not be created. Defaults to false.
244+
DESCRIPTION
245+
}
246+
227247
variable "power_platform_environment" {
228248
type = object({
229249
name = string

0 commit comments

Comments
 (0)