Skip to content

Commit 5eabd5d

Browse files
Initial Code Migration (#30)
* Fixing a typo * Initial migration attempt * Update .gitignore Co-authored-by: Matt Dotson <[email protected]> * Updates per review comments --------- Co-authored-by: Matt Dotson <[email protected]>
1 parent 73c5adf commit 5eabd5d

19 files changed

+1696
-58
lines changed

.gitignore

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,4 +399,50 @@ FodyWeavers.xsd
399399
# JetBrains Rider
400400
*.sln.iml
401401

402-
.azure/
402+
# Local .terraform directories
403+
**/.terraform/*
404+
405+
# .tfstate files
406+
*.tfstate
407+
*.tfstate.*
408+
409+
# Crash log files
410+
crash.log
411+
crash.*.log
412+
413+
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
414+
# password, private keys, and other secrets. These should not be part of version
415+
# control as they are data points which are potentially sensitive and subject
416+
# to change depending on the environment.
417+
418+
*.tfvars
419+
*.tfvars.json
420+
421+
# Ignore override files as they are usually used to override resources locally and so
422+
# are not checked in
423+
override.tf
424+
override.tf.json
425+
*_override.tf
426+
*_override.tf.json
427+
428+
# Include override files you do wish to add to version control using negated pattern
429+
# Ignore Terraform lock file
430+
.terraform.lock.hcl
431+
432+
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
433+
# example: *tfplan*
434+
435+
# Ignore CLI configuration files
436+
.terraformrc
437+
terraform.rc
438+
439+
# Plan files
440+
*.tfplan
441+
442+
# Test binaries and temporary files
443+
*.test
444+
*.out
445+
/test-logs/
446+
447+
# Checkov reports
448+
checkov_report.*

README.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
# Project Name
22

3-
(short, 1-3 sentenced, description of the project)
3+
This repository provides a baseline architecture for integrating Copilot Studio and Power Platform with Azure AI resources. It addresses challenges in initializing and managing these connections while prioritizing enterprise readiness. Key features include robust network configuration, observability tools, and secure, scalable authentication.
44

55
## Features
66

7-
This project framework provides the following features:
8-
9-
* Feature 1
10-
* Feature 2
11-
* ...
7+
* Seamless integration of Copilot Studio with Azure AI resources.
8+
* Enterprise-grade network configuration for secure and scalable deployments.
9+
* Observability tools for monitoring and troubleshooting.
10+
* Secure authentication mechanisms aligned with enterprise standards.
11+
* Modular Terraform code structure for easy customization and reuse.
12+
* Support for remote state management using Azure Storage.
13+
* Automated resource tagging for better organization and cost tracking.
14+
* Validation of input variables to ensure robust deployments.
15+
* Pre-configured backend setup for remote state storage.
16+
* Documentation and examples for quick onboarding and usage.
1217

1318
## Getting Started
1419

1520
### Prerequisites
1621

17-
(ideally very short, if any)
18-
19-
- OS
20-
- Library version
21-
- ...
22+
To use this example, you must complete the following prerequisites:
23+
- Set up a service principal with the permissions outlined in the [Power Platform Terraform Provider's documentation](https://microsoft.github.io/terraform-provider-power-platform/guides/app_registration/)
24+
- Set up an interactive user with sufficient Power Platform licensing to interact with the resources managed by this module.
2225

2326
### Installation
2427

infra/main.ai.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module "azure_open_ai" {
2+
source = "Azure/avm-res-cognitiveservices-account/azurerm"
3+
version = "0.6.0"
4+
kind = "OpenAI"
5+
location = var.location
6+
name = "aoai${random_string.name.id}"
7+
resource_group_name = azurerm_resource_group.this.name
8+
enable_telemetry = true
9+
sku_name = "S0"
10+
local_auth_enabled = true
11+
cognitive_deployments = var.cognitive_deployments
12+
network_acls = {
13+
default_action = "Deny"
14+
}
15+
managed_identities = {
16+
system_assigned = true
17+
}
18+
tags = var.tags
19+
}

infra/main.app_insights.tf

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
resource "random_uuid" "uid" {}
2+
3+
resource "azurerm_application_insights" "insights" {
4+
count = var.include_app_insights ? 1 : 0
5+
6+
application_type = "web"
7+
location = azurerm_resource_group.this.location
8+
name = "${var.resource_prefix}-appinsights-${var.resource_suffix}"
9+
resource_group_name = azurerm_resource_group.this.name
10+
}
11+
12+
resource "azurerm_application_insights_workbook" "workbook" {
13+
count = var.include_app_insights ? 1 : 0
14+
15+
data_json = jsonencode({
16+
"version" : "Notebook/1.0",
17+
"items" : [
18+
{
19+
"type" : 1,
20+
"content" : {
21+
"json" : "${var.app_insights_workbook_description}",
22+
"style" : "info"
23+
},
24+
"name" : "Notebook description"
25+
},
26+
{
27+
"type" : 3,
28+
"content" : {
29+
"version" : "KqlItem/1.0",
30+
"query" : "${var.app_insights_sections["section_1"].query}",
31+
"size" : 0,
32+
"timeContext" : {
33+
"durationMs" : 1800000
34+
},
35+
"queryType" : 0,
36+
"resourceType" : "microsoft.insights/components",
37+
"crossComponentResources" : [
38+
"/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.this.name}/providers/Microsoft.Insights/components/${azurerm_application_insights.insights[0].name}"
39+
],
40+
"visualization" : "${var.app_insights_sections["section_1"].chart}"
41+
},
42+
"name" : "${var.app_insights_sections["section_1"].name}"
43+
},
44+
{
45+
"type" : 3,
46+
"content" : {
47+
"version" : "KqlItem/1.0",
48+
"query" : "${var.app_insights_sections["section_2"].query}",
49+
"size" : 0,
50+
"timeContext" : {
51+
"durationMs" : 1800000
52+
},
53+
"queryType" : 0,
54+
"resourceType" : "microsoft.insights/components",
55+
"crossComponentResources" : [
56+
"/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.this.name}/providers/Microsoft.Insights/components/${azurerm_application_insights.insights[0].name}"
57+
],
58+
"visualization" : "${var.app_insights_sections["section_2"].chart}"
59+
},
60+
"name" : "${var.app_insights_sections["section_2"].name}"
61+
},
62+
{
63+
"type" : 3,
64+
"content" : {
65+
"version" : "KqlItem/1.0",
66+
"query" : "${var.app_insights_sections["section_3"].query}",
67+
"size" : 0,
68+
"timeContext" : {
69+
"durationMs" : 1800000
70+
},
71+
"queryType" : 0,
72+
"resourceType" : "microsoft.insights/components",
73+
"crossComponentResources" : [
74+
"/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.this.name}/providers/Microsoft.Insights/components/${azurerm_application_insights.insights[0].name}"
75+
],
76+
"visualization" : "${var.app_insights_sections["section_3"].chart}"
77+
},
78+
"name" : "${var.app_insights_sections["section_3"].name}"
79+
},
80+
{
81+
"type" : 3,
82+
"content" : {
83+
"version" : "KqlItem/1.0",
84+
"query" : "${var.app_insights_sections["section_4"].query}",
85+
"size" : 0,
86+
"timeContext" : {
87+
"durationMs" : 1800000
88+
},
89+
"queryType" : 0,
90+
"resourceType" : "microsoft.insights/components",
91+
"crossComponentResources" : [
92+
"/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.this.name}/providers/Microsoft.Insights/components/${azurerm_application_insights.insights[0].name}"
93+
],
94+
"visualization" : "${var.app_insights_sections["section_4"].chart}"
95+
},
96+
"name" : "${var.app_insights_sections["section_4"].name}"
97+
},
98+
{
99+
"type" : 3,
100+
"content" : {
101+
"version" : "KqlItem/1.0",
102+
"query" : "${var.app_insights_sections["section_5"].query}",
103+
"size" : 0,
104+
"timeContext" : {
105+
"durationMs" : 1800000
106+
},
107+
"queryType" : 0,
108+
"resourceType" : "microsoft.insights/components",
109+
"crossComponentResources" : [
110+
"/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.this.name}/providers/Microsoft.Insights/components/${azurerm_application_insights.insights[0].name}"
111+
],
112+
"visualization" : "${var.app_insights_sections["section_5"].chart}"
113+
},
114+
"name" : "${var.app_insights_sections["section_5"].name}"
115+
},
116+
{
117+
"type" : 3,
118+
"content" : {
119+
"version" : "KqlItem/1.0",
120+
"query" : "${var.app_insights_sections["section_6"].query}",
121+
"size" : 0,
122+
"timeContext" : {
123+
"durationMs" : 1800000
124+
},
125+
"queryType" : 0,
126+
"resourceType" : "microsoft.insights/components",
127+
"crossComponentResources" : [
128+
"/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.this.name}/providers/Microsoft.Insights/components/${azurerm_application_insights.insights[0].name}"
129+
],
130+
"visualization" : "${var.app_insights_sections["section_6"].chart}"
131+
},
132+
"name" : "${var.app_insights_sections["section_6"].name}"
133+
}
134+
],
135+
"fallbackResourceIds" : [
136+
"azure monitor"
137+
],
138+
"$schema" : "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json"
139+
})
140+
display_name = "Azure Monitor Workbook"
141+
location = azurerm_resource_group.this.location
142+
name = random_uuid.uid.result
143+
resource_group_name = azurerm_resource_group.this.name
144+
}

infra/main.network.tf

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
module "primary_virtual_network" {
2+
source = "Azure/avm-res-network-virtualnetwork/azurerm"
3+
version = "0.8.0"
4+
resource_group_name = azurerm_resource_group.this.name
5+
subnets = merge(
6+
{
7+
"${var.primary_subnet_name}" = {
8+
name = var.primary_subnet_name
9+
address_prefixes = var.primary_subnet_address_spaces
10+
service_endpoints = ["Microsoft.Storage"]
11+
delegation = [{
12+
name = "Microsoft.PowerPlatform/enterprisePolicies"
13+
service_delegation = {
14+
name = "Microsoft.PowerPlatform/enterprisePolicies"
15+
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
16+
}
17+
}
18+
]
19+
nat_gateway = {
20+
id = azurerm_nat_gateway.primary_nat_gateway.id
21+
}
22+
}
23+
},
24+
{
25+
ai-search-primary-subnet = {
26+
name = "ai-search-primary-subnet"
27+
address_prefixes = var.primary_ai_search_subnet_address_spaces
28+
nat_gateway = {
29+
id = azurerm_nat_gateway.primary_nat_gateway.id
30+
}
31+
}
32+
}
33+
)
34+
address_space = var.primary_vnet_address_spaces
35+
location = var.primary_location
36+
name = "power-platform-primary-vnet-${random_string.name.id}"
37+
tags = var.tags
38+
}
39+
40+
module "failover_virtual_network" {
41+
source = "Azure/avm-res-network-virtualnetwork/azurerm"
42+
version = "0.8.0"
43+
resource_group_name = azurerm_resource_group.this.name
44+
subnets = merge(
45+
{
46+
"${var.failover_subnet_name}" = {
47+
name = var.failover_subnet_name
48+
address_prefixes = var.failover_subnet_address_spaces
49+
service_endpoints = ["Microsoft.Storage"]
50+
delegation = [{
51+
name = "Microsoft.PowerPlatform/enterprisePolicies"
52+
service_delegation = {
53+
name = "Microsoft.PowerPlatform/enterprisePolicies"
54+
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
55+
}
56+
}
57+
]
58+
nat_gateway = {
59+
id = azurerm_nat_gateway.failover_nat_gateway.id
60+
}
61+
}
62+
},
63+
{
64+
ai-search-failover-subnet = {
65+
name = "ai-search-failover-subnet"
66+
address_prefixes = var.failover_ai_search_subnet_address_spaces
67+
nat_gateway = {
68+
id = azurerm_nat_gateway.failover_nat_gateway.id
69+
}
70+
}
71+
}
72+
)
73+
address_space = var.failover_vnet_address_spaces
74+
location = var.failover_location
75+
name = "power-platform-failover-vnet-${random_string.name.id}"
76+
tags = var.tags
77+
}
78+
79+
#---- Set up NAT gateways, which are not initialized by the AVM ----
80+
81+
# Primary VNet NAT gateway
82+
resource "azurerm_nat_gateway" "primary_nat_gateway" {
83+
location = var.primary_location
84+
name = "primary-nat-gateway"
85+
resource_group_name = azurerm_resource_group.this.name
86+
sku_name = "Standard"
87+
}
88+
89+
# Secondary VNet NAT gateway
90+
91+
resource "azurerm_nat_gateway" "failover_nat_gateway" {
92+
location = var.failover_location
93+
name = "failover-nat-gateway"
94+
resource_group_name = azurerm_resource_group.this.name
95+
sku_name = "Standard"
96+
}
97+
98+
# TODO add a proper polling mechanism instead of wait
99+
resource "time_sleep" "wait_for_network" {
100+
create_duration = "30s" # Wait for 30 seconds
101+
102+
depends_on = [module.primary_virtual_network, module.failover_virtual_network]
103+
}

0 commit comments

Comments
 (0)