Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions .github/workflows/azure-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ on:
# Set this to the mainline branch you are using
branches:
- main
pull_request:
# Run when pull requests are opened or updated
branches:
- main
# GitHub Actions workflow to deploy to Azure using azd

permissions:
actions: read # Needed for uploading SARIF reports
security-events: write # Needed for uploading SARIF reports
Expand All @@ -33,22 +36,20 @@ jobs:
build:
runs-on: ${{ fromJson(vars.ACTIONS_RUNNER_NAME || '["ubuntu-latest"]') }}
env:
AZURE_ENV_NAME: ${{ github.event.inputs.azd_environment_name || 'CICD' }}
AZURE_ENV_NAME: ${{ github.event.inputs.azd_environment_name || (github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number)) || 'CICD' }}
AZURE_LOCATION: ${{ github.event.inputs.azure_location || 'eastus' }}

steps:
- name: Checkout the branch ${{ github.ref_name }}
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.ref_name }}
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Install azd
uses: Azure/setup-azd@cf638ffd167fc81e1851241a478a723c05fa9cb3 # v2.2.0
uses: Azure/setup-azd@cf638ffd167fc81e1851241a478a723c05fa9cb3 # v2.2.0
with:
version: '1.20.0' # Specify your desired azd version here

- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: '18.x'

Expand All @@ -58,7 +59,7 @@ jobs:
terraform_version: 1.13.3

- name: Install TFLint
uses: terraform-linters/setup-tflint@acd1575d3c037258ce5b2dd01379dc49ce24c6b7 # v6.2.0
uses: terraform-linters/setup-tflint@acd1575d3c037258ce5b2dd01379dc49ce24c6b7 # v6.2.0
with:
tflint_version: v0.58.1
github_token: ${{ secrets.GITHUB_TOKEN }} # Used to avoid rate
Expand Down Expand Up @@ -86,9 +87,21 @@ jobs:
echo "GitLeaks scan completed"

- name: Setup .NET SDK
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
with:
dotnet-version: '8.0.x'
shell: bash
run: |
# Install .NET SDK to temp directory for self-hosted runners to avoid permission issues
DOTNET_INSTALL_DIR="${{ runner.temp }}/dotnet"
mkdir -p "$DOTNET_INSTALL_DIR"

# Download and run the dotnet-install script
curl -sSL https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh
chmod +x dotnet-install.sh
./dotnet-install.sh --channel 9.0 --install-dir "$DOTNET_INSTALL_DIR"
rm dotnet-install.sh

# Add to PATH for subsequent steps
echo "$DOTNET_INSTALL_DIR" >> $GITHUB_PATH
echo "DOTNET_ROOT=$DOTNET_INSTALL_DIR" >> $GITHUB_ENV

- name: Install Power Platform Tools
uses: microsoft/powerplatform-actions/actions-install@6c7b538671a040d11afd8ab94d77bfe3b3ed87e6 # v1.9.1
Expand All @@ -100,7 +113,7 @@ jobs:
pac help

- name: Set Up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # 6.0.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # 6.0.0
with:
python-version: "3.x"

Expand Down Expand Up @@ -181,7 +194,7 @@ jobs:
sarif_file: ./checkov-results.sarif/results_sarif.sarif

- name: Azd down
if: ${{ github.event.inputs.run_azd_down == 'true' }}
if: ${{ github.event.inputs.run_azd_down == 'true' || github.event_name == 'pull_request' }}
env:
POWER_PLATFORM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
POWER_PLATFORM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
Expand Down
2 changes: 1 addition & 1 deletion docs/app_registration_setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ To enable secure automation and integration with Azure and Power Platform, you n
1. Login to your Power Platform:

```shell
pac auth create
pac auth create --deviceCode
```

1. Create new **App Registration**:
Expand Down
2 changes: 1 addition & 1 deletion docs/cicd.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ All infrastructure for CI/CD lives under `cicd/` and can be customized to meet y

## Prerequisites

- Working local environment of this template. If you do not have one, Follow the step by step instructions for setting up your [**Local Environment**](../README.md#local-environment)
- Working local environment of this template. If you do not have one, Follow the step by step instructions for setting up your [**Local Environment**](../README.md#local-environment).
- An Azure subscription with either User Access Administrator or Owner permissions to create workload identity resources like service principal, and OIDC to be used by the GitHub Actions.
- GitHub CLI (`gh`) installed and authenticated to trigger the bootstrap workflow from your terminal.

Expand Down
13 changes: 13 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Troubleshooting tips

## Quota error during deployment

If you see an InsufficientQuota error mentioning "Tokens Per Minute", the requested `scale.capacity` (thousands of TPM) exceeds your subscription's available quota — lower `scale.capacity` in TFVARS or request a quota increase in the Azure portal.

## Private endpoint fails with AccountProvisioningStateInvalid

This occurs when Terraform tries to create the private endpoint before the Azure OpenAI (Cognitive Services) account leaves the `Accepted` state; wait until the resource shows `Succeeded` (portal or `az resource show`) and re-run the provisioning (`azd provision`).

## Use GitHub Copilot to help troubleshoot

If you're unsure how to fix a deployment error, open the relevant files in VS Code and use GitHub Copilot for suggestions. Copilot can propose TFVARS overrides, sample values, terraform plan snippets, or concise support-request wording; always review and test generated suggestions before applying them.
50 changes: 40 additions & 10 deletions infra/main.ai.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

# Wait for network infrastructure to be ready
resource "time_sleep" "wait_for_network_ready" {
depends_on = [
module.copilot_studio
]
create_duration = "30s"
}

module "azure_open_ai" {
# checkov:skip=CKV2_AZURE_22: Customer-managed keys should be added in production usage but are not included here for simplicity.
# checkov:skip=CKV_AZURE_236: The Power Platform AI Search connector only supports service principal, API key, or interactive auth.
Expand Down Expand Up @@ -32,19 +40,41 @@ module "azure_open_ai" {
]
}

private_endpoints = {
pe_endpoint = {
name = "pe-${azurecaf_name.main_names.results["azurerm_cognitive_account"]}"
private_service_connection_name = "pe_endpoint_connection"
subnet_resource_id = local.pe_primary_subnet_id
}
}
managed_identities = {
system_assigned = true
}
tags = var.tags

depends_on = [module.copilot_studio]
depends_on = [time_sleep.wait_for_network_ready]
}

# Wait for Azure OpenAI service to be fully provisioned
resource "time_sleep" "wait_for_openai_provisioning" {
depends_on = [module.azure_open_ai]
create_duration = "60s"

# Ensure the OpenAI service is in a ready state before proceeding with private endpoint creation
triggers = {
openai_id = module.azure_open_ai.resource.id
}
}

# Create private endpoint separately to ensure OpenAI service is fully ready
resource "azurerm_private_endpoint" "openai_pe" {
name = "pe-${azurecaf_name.main_names.results["azurerm_cognitive_account"]}"
location = local.primary_azure_region
resource_group_name = local.resource_group_name
subnet_id = local.pe_primary_subnet_id
tags = var.tags

private_service_connection {
name = "pe_endpoint_connection"
private_connection_resource_id = module.azure_open_ai.resource.id
subresource_names = ["account"]
is_manual_connection = false
}

depends_on = [time_sleep.wait_for_openai_provisioning]
}

# Private DNS zone for Azure OpenAI private endpoint resolution
Expand All @@ -69,12 +99,12 @@ resource "azurerm_private_dns_zone_virtual_network_link" "aoai_dns_links" {
}

# DNS A record for Azure OpenAI private endpoint
# The module creates the private endpoint, so we reference it from the module outputs
# Reference the separately created private endpoint
resource "azurerm_private_dns_a_record" "aoai_dns_record" {
name = module.azure_open_ai.resource.name
zone_name = azurerm_private_dns_zone.aoai_dns.name
resource_group_name = local.resource_group_name
ttl = 10
records = [module.azure_open_ai.private_endpoints["pe_endpoint"].private_service_connection[0].private_ip_address]
records = [azurerm_private_endpoint.openai_pe.private_service_connection[0].private_ip_address]
tags = var.tags
}
14 changes: 14 additions & 0 deletions infra/main.app_insights.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,27 @@

resource "random_uuid" "uid" {}

resource "azurerm_log_analytics_workspace" "monitoring" {
count = var.include_log_analytics ? 1 : 0

daily_quota_gb = -1
location = local.primary_azure_region
name = azurecaf_name.main_names.results["azurerm_log_analytics_workspace"]
resource_group_name = local.resource_group_name
retention_in_days = var.log_analytics_retention_in_days
sku = "PerGB2018"
tags = var.tags
}

resource "azurerm_application_insights" "insights" {
count = var.include_app_insights ? 1 : 0

application_type = "web"
location = local.primary_azure_region
name = "${var.resource_prefix}-appinsights-${var.resource_suffix}"
resource_group_name = local.resource_group_name
workspace_id = var.include_log_analytics ? azurerm_log_analytics_workspace.monitoring[0].id : null
tags = var.tags
}

resource "azurerm_application_insights_workbook" "workbook" {
Expand Down
Loading
Loading