Skip to content

Commit 1949de7

Browse files
committed
finalized deployment code
1 parent e1aaa67 commit 1949de7

23 files changed

Lines changed: 445 additions & 86 deletions

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,9 @@ azd env new <environment-name>
214214
azd up
215215
```
216216

217-
Before running `azd up`, review the detailed guide and set any environment-specific values you need, especially networking, host counts, VM sizes, and SQL firewall access. The deployment scripts under `deploy/` now handle the Entra bootstrap, SSH key flow, post-provision role assignment, container image builds, SQL initialization, and Linux host SQL registration used by this solution.
217+
The deployment defaults the App Service plan to Premium v3 `P2mv3`, which provides the minimum supported baseline of 4 vCPUs and 32 GB memory for the frontend, API, and task apps.
218+
219+
Before running `azd up`, review the detailed guide and set any environment-specific values you need, especially networking, host counts, VM sizes, App Service plan sizing, and SQL firewall access. The deployment scripts under `deploy/` now handle the Entra bootstrap, SSH key flow, App Service health checks on `/health`, Application Insights wiring for the frontend and API, post-provision role assignment, container image builds, SQL initialization, and Linux host SQL registration used by this solution.
218220

219221
## Contributing
220222

api/app.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
import logging
1212
import re
1313

14+
from azure.monitor.opentelemetry import configure_azure_monitor
15+
16+
connection_string = os.environ.get('APPLICATIONINSIGHTS_CONNECTION_STRING')
17+
if connection_string:
18+
configure_azure_monitor(connection_string=connection_string, logger_name='linuxbroker.api')
19+
1420
from flask import Flask, jsonify, request
1521
from azure.identity import DefaultAzureCredential
1622
from azure.mgmt.compute import ComputeManagementClient
@@ -31,7 +37,25 @@
3137
# Logging Configuration
3238

3339
logging.basicConfig(level=logging.INFO)
34-
logger = logging.getLogger(__name__)
40+
logger = logging.getLogger('linuxbroker.api')
41+
42+
43+
@app.route('/health', methods=['GET'])
44+
def health():
45+
conn = get_db_connection()
46+
if not conn:
47+
return jsonify({'status': 'unhealthy'}), 503
48+
49+
try:
50+
cursor = conn.cursor()
51+
cursor.execute('SELECT 1')
52+
cursor.fetchone()
53+
return jsonify({'status': 'healthy', 'version': app.config['VERSION']}), 200
54+
except Exception as e:
55+
logger.error("Health check failed: %s", e)
56+
return jsonify({'status': 'unhealthy'}), 503
57+
finally:
58+
conn.close()
3559

3660
# ===============================
3761
# Functions
@@ -433,7 +457,7 @@ def get_all_vms():
433457
conn.close()
434458

435459
if not rows:
436-
return "No VMs found.", 404
460+
return jsonify([]), 200
437461

438462
return jsonify(rows), 200
439463

api/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Flask==2.2.2
22
gunicorn
33
Werkzeug==2.2.2
44
pyodbc==4.0.35
5+
azure-monitor-opentelemetry==1.8.7
56
azure-identity==1.17.1
67
azure-mgmt-compute==33.0.0
78
pyjwt==2.9.0

custom_script_extensions/Configure-RHEL7-Host.sh

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22

33
# Installs and configures the necessary packages for Linux Broker for AVD Access on RHEL 7
44

5+
LINUXBROKER_API_BASE_URL="${1:-}"
6+
LINUXBROKER_API_CLIENT_ID="${2:-}"
7+
8+
if [ -z "$LINUXBROKER_API_BASE_URL" ] || [ -z "$LINUXBROKER_API_CLIENT_ID" ]; then
9+
echo "Linux Broker API base URL and client ID are required."
10+
exit 1
11+
fi
12+
13+
case "$LINUXBROKER_API_BASE_URL" in
14+
https://*) ;;
15+
*)
16+
echo "Linux Broker API base URL must start with https://"
17+
exit 1
18+
;;
19+
esac
20+
21+
LINUXBROKER_API_BASE_URL="${LINUXBROKER_API_BASE_URL%/}"
22+
523
# ===============================
624
# Variables
725

@@ -28,8 +46,8 @@ CURRENT_USERS_DETAILS="$output_directory/xrdp-loggedin-users.txt"
2846

2947
CRON_SCHEDULE="0 * * * *"
3048

31-
YOUR_LINUXBROKER_API_CLIENT_ID="my_actual_client_id"
32-
YOUR_LINUXBROKER_API_URL="my.actual.linuxbroker.api.url"
49+
YOUR_LINUXBROKER_API_CLIENT_ID="$LINUXBROKER_API_CLIENT_ID"
50+
YOUR_LINUXBROKER_API_BASE_URL="$LINUXBROKER_API_BASE_URL"
3351

3452
# ===============================
3553
# Execution
@@ -122,7 +140,8 @@ echo "Downloading release-session.sh..."
122140
sudo wget -O "$SCRIPT_PATH" "$release_session_url"
123141

124142
sudo sed -i "s|YOUR_LINUX_BROKER_API_CLIENT_ID|$YOUR_LINUXBROKER_API_CLIENT_ID|g" "$SCRIPT_PATH"
125-
sudo sed -i "s|YOUR_LINUX_BROKER_API_URL|$YOUR_LINUXBROKER_API_URL|g" "$SCRIPT_PATH"
143+
sudo sed -i "s|YOUR_LINUX_BROKER_API_BASE_URL|$YOUR_LINUXBROKER_API_BASE_URL|g" "$SCRIPT_PATH"
144+
sudo sed -i "s|YOUR_LINUX_BROKER_API_URL|$YOUR_LINUXBROKER_API_BASE_URL|g" "$SCRIPT_PATH"
126145

127146
echo "Downloading xrdp-who-xnc.sh..."
128147
sudo wget -O "$output_directory/xrdp-who-xnc.sh" "$xrdp_who_xnc_url"

custom_script_extensions/Configure-RHEL8-Host.sh

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22

33
# Installs and configures the necessary packages for Linux Broker for AVD Access on RHEL 8
44

5+
LINUXBROKER_API_BASE_URL="${1:-}"
6+
LINUXBROKER_API_CLIENT_ID="${2:-}"
7+
8+
if [ -z "$LINUXBROKER_API_BASE_URL" ] || [ -z "$LINUXBROKER_API_CLIENT_ID" ]; then
9+
echo "Linux Broker API base URL and client ID are required."
10+
exit 1
11+
fi
12+
13+
case "$LINUXBROKER_API_BASE_URL" in
14+
https://*) ;;
15+
*)
16+
echo "Linux Broker API base URL must start with https://"
17+
exit 1
18+
;;
19+
esac
20+
21+
LINUXBROKER_API_BASE_URL="${LINUXBROKER_API_BASE_URL%/}"
22+
523
# ===============================
624
# Variables
725

@@ -50,8 +68,8 @@ CURRENT_USERS_DETAILS="$output_directory/xrdp-loggedin-users.txt"
5068

5169
CRON_SCHEDULE="0 * * * *"
5270

53-
YOUR_LINUXBROKER_API_CLIENT_ID="my_actual_client_id"
54-
YOUR_LINUXBROKER_API_URL="my.actual.linuxbroker.api.url"
71+
YOUR_LINUXBROKER_API_CLIENT_ID="$LINUXBROKER_API_CLIENT_ID"
72+
YOUR_LINUXBROKER_API_BASE_URL="$LINUXBROKER_API_BASE_URL"
5573

5674
# ===============================
5775
# Execution
@@ -145,7 +163,8 @@ echo "Downloading release-session.sh..."
145163
sudo wget -O "$SCRIPT_PATH" "$release_session_url"
146164

147165
sudo sed -i "s|YOUR_LINUX_BROKER_API_CLIENT_ID|$YOUR_LINUXBROKER_API_CLIENT_ID|g" "$SCRIPT_PATH"
148-
sudo sed -i "s|YOUR_LINUX_BROKER_API_URL|$YOUR_LINUXBROKER_API_URL|g" "$SCRIPT_PATH"
166+
sudo sed -i "s|YOUR_LINUX_BROKER_API_BASE_URL|$YOUR_LINUXBROKER_API_BASE_URL|g" "$SCRIPT_PATH"
167+
sudo sed -i "s|YOUR_LINUX_BROKER_API_URL|$YOUR_LINUXBROKER_API_BASE_URL|g" "$SCRIPT_PATH"
149168

150169
echo "Downloading xrdp-who-xorg.sh..."
151170
sudo wget -O "$output_directory/xrdp-who-xorg.sh" "$xrdp_who_xorg_url"

custom_script_extensions/Configure-RHEL9-Host.sh

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
# Installs and configures the necessary packages for Linux Broker for AVD Access on RHEL 9
44

5+
LINUXBROKER_API_BASE_URL="${1:-}"
6+
LINUXBROKER_API_CLIENT_ID="${2:-}"
7+
8+
if [[ -z "$LINUXBROKER_API_BASE_URL" || -z "$LINUXBROKER_API_CLIENT_ID" ]]; then
9+
echo "Linux Broker API base URL and client ID are required."
10+
exit 1
11+
fi
12+
13+
if [[ "$LINUXBROKER_API_BASE_URL" != https://* ]]; then
14+
echo "Linux Broker API base URL must start with https://"
15+
exit 1
16+
fi
17+
18+
LINUXBROKER_API_BASE_URL="${LINUXBROKER_API_BASE_URL%/}"
19+
520
# ===============================
621
# Variables
722

@@ -28,8 +43,8 @@ CURRENT_USERS_DETAILS="$output_directory/xrdp-loggedin-users.txt"
2843

2944
CRON_SCHEDULE="0 * * * *"
3045

31-
YOUR_LINUXBROKER_API_CLIENT_ID="my_actual_client_id"
32-
YOUR_LINUXBROKER_API_URL="my.actual.linuxbroker.api.url"
46+
YOUR_LINUXBROKER_API_CLIENT_ID="$LINUXBROKER_API_CLIENT_ID"
47+
YOUR_LINUXBROKER_API_BASE_URL="$LINUXBROKER_API_BASE_URL"
3348

3449
# ===============================
3550
# Execution
@@ -134,7 +149,8 @@ echo "Downloading release-session.sh..."
134149
sudo wget -O "$SCRIPT_PATH" "$release_session_url"
135150

136151
sudo sed -i "s|YOUR_LINUX_BROKER_API_CLIENT_ID|$YOUR_LINUXBROKER_API_CLIENT_ID|g" "$SCRIPT_PATH"
137-
sudo sed -i "s|YOUR_LINUX_BROKER_API_URL|$YOUR_LINUXBROKER_API_URL|g" "$SCRIPT_PATH"
152+
sudo sed -i "s|YOUR_LINUX_BROKER_API_BASE_URL|$YOUR_LINUXBROKER_API_BASE_URL|g" "$SCRIPT_PATH"
153+
sudo sed -i "s|YOUR_LINUX_BROKER_API_URL|$YOUR_LINUXBROKER_API_BASE_URL|g" "$SCRIPT_PATH"
138154

139155
echo "Downloading xrdp-who-xnc.sh..."
140156
sudo wget -O "$output_directory/xrdp-who-xnc.sh" "$xrdp_who_xnc_url"

custom_script_extensions/Configure-Ubuntu24_desktop-Host.sh

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
# Installs and configures the necessary packages for Linux Broker for AVD Access on Ubuntu 24 desktop
44

5+
LINUXBROKER_API_BASE_URL="${1:-}"
6+
LINUXBROKER_API_CLIENT_ID="${2:-}"
7+
8+
if [[ -z "$LINUXBROKER_API_BASE_URL" || -z "$LINUXBROKER_API_CLIENT_ID" ]]; then
9+
echo "Linux Broker API base URL and client ID are required."
10+
exit 1
11+
fi
12+
13+
if [[ "$LINUXBROKER_API_BASE_URL" != https://* ]]; then
14+
echo "Linux Broker API base URL must start with https://"
15+
exit 1
16+
fi
17+
18+
LINUXBROKER_API_BASE_URL="${LINUXBROKER_API_BASE_URL%/}"
19+
520
# ===============================
621
# Variables
722

@@ -20,8 +35,8 @@ CURRENT_USERS_DETAILS="$output_directory/xrdp-loggedin-users.txt"
2035

2136
CRON_SCHEDULE="0 * * * *"
2237

23-
YOUR_LINUXBROKER_API_CLIENT_ID="my_actual_client_id"
24-
YOUR_LINUXBROKER_API_URL="my.actual.linuxbroker.api.url"
38+
YOUR_LINUXBROKER_API_CLIENT_ID="$LINUXBROKER_API_CLIENT_ID"
39+
YOUR_LINUXBROKER_API_BASE_URL="$LINUXBROKER_API_BASE_URL"
2540

2641
# ===============================
2742
# Execution
@@ -108,7 +123,8 @@ echo "Downloading release-session.sh..."
108123
sudo wget -O "$SCRIPT_PATH" "$release_session_url"
109124

110125
sudo sed -i "s|YOUR_LINUX_BROKER_API_CLIENT_ID|$YOUR_LINUXBROKER_API_CLIENT_ID|g" "$SCRIPT_PATH"
111-
sudo sed -i "s|YOUR_LINUX_BROKER_API_URL|$YOUR_LINUXBROKER_API_URL|g" "$SCRIPT_PATH"
126+
sudo sed -i "s|YOUR_LINUX_BROKER_API_BASE_URL|$YOUR_LINUXBROKER_API_BASE_URL|g" "$SCRIPT_PATH"
127+
sudo sed -i "s|YOUR_LINUX_BROKER_API_URL|$YOUR_LINUXBROKER_API_BASE_URL|g" "$SCRIPT_PATH"
112128

113129
echo "Downloading xrdp-who-xnc.sh..."
114130
sudo wget -O "$output_directory/xrdp-who-xnc.sh" "$xrdp_who_xnc_url"

deploy/Build-ContainerImages.ps1

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,50 @@ param(
2222
Set-StrictMode -Version Latest
2323
$ErrorActionPreference = 'Stop'
2424

25+
function Invoke-AzCommandWithRetry {
26+
param(
27+
[Parameter(Mandatory = $true)]
28+
[scriptblock]$Command,
29+
30+
[Parameter(Mandatory = $true)]
31+
[string]$Description,
32+
33+
[Parameter(Mandatory = $false)]
34+
[int]$MaxAttempts = 4,
35+
36+
[Parameter(Mandatory = $false)]
37+
[int]$InitialDelaySeconds = 5
38+
)
39+
40+
$lastError = ''
41+
42+
for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
43+
$commandOutput = & $Command 2>&1 | Out-String
44+
if ($LASTEXITCODE -eq 0) {
45+
return
46+
}
47+
48+
$lastError = $commandOutput.Trim()
49+
if ($attempt -ge $MaxAttempts) {
50+
break
51+
}
52+
53+
$delaySeconds = [Math]::Min($InitialDelaySeconds * [Math]::Pow(2, $attempt - 1), 30)
54+
Write-Warning "$Description failed on attempt $attempt of $MaxAttempts. Retrying in $([int]$delaySeconds) seconds."
55+
if (-not [string]::IsNullOrWhiteSpace($lastError)) {
56+
Write-Warning $lastError
57+
}
58+
59+
Start-Sleep -Seconds ([int]$delaySeconds)
60+
}
61+
62+
if ([string]::IsNullOrWhiteSpace($lastError)) {
63+
throw "Failed to $Description after $MaxAttempts attempts."
64+
}
65+
66+
throw "Failed to $Description after $MaxAttempts attempts. Last error: $lastError"
67+
}
68+
2569
if ([string]::IsNullOrWhiteSpace($EnvironmentName)) {
2670
$EnvironmentName = if (-not [string]::IsNullOrWhiteSpace($env:AZURE_ENV_NAME)) {
2771
$env:AZURE_ENV_NAME
@@ -91,19 +135,16 @@ try {
91135
}
92136
}
93137

94-
az webapp restart --name $FrontendAppName --resource-group $ResourceGroupName | Out-Null
95-
if ($LASTEXITCODE -ne 0) {
96-
throw "Failed to restart frontend app '$FrontendAppName'."
138+
Invoke-AzCommandWithRetry -Description "restart frontend app '$FrontendAppName'" -Command {
139+
az webapp restart --name $FrontendAppName --resource-group $ResourceGroupName --only-show-errors --output none
97140
}
98141

99-
az webapp restart --name $ApiAppName --resource-group $ResourceGroupName | Out-Null
100-
if ($LASTEXITCODE -ne 0) {
101-
throw "Failed to restart API app '$ApiAppName'."
142+
Invoke-AzCommandWithRetry -Description "restart API app '$ApiAppName'" -Command {
143+
az webapp restart --name $ApiAppName --resource-group $ResourceGroupName --only-show-errors --output none
102144
}
103145

104-
az functionapp restart --name $TaskAppName --resource-group $ResourceGroupName | Out-Null
105-
if ($LASTEXITCODE -ne 0) {
106-
throw "Failed to restart task app '$TaskAppName'."
146+
Invoke-AzCommandWithRetry -Description "restart task app '$TaskAppName'" -Command {
147+
az functionapp restart --name $TaskAppName --resource-group $ResourceGroupName --only-show-errors --output none
107148
}
108149
}
109150
finally {

deploy/DEPLOYMENT.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ The checked-in [bicep/main.parameters.example.json](bicep/main.parameters.exampl
7878

7979
- `appName`: base name for generated resources.
8080
- `AZURE_LOCATION`: deployment region.
81+
- `appServicePlanSku`: App Service plan SKU. The deployment baseline defaults to Premium v3 `P2mv3` for 4 vCPUs and 32 GB memory.
8182
- `allowedClientIp`: your public client IP for SQL bootstrap from the local machine.
8283
- `deployLinuxHosts`: `true` or `false`.
8384
- `deployAvdHosts`: `true` or `false`.
@@ -190,6 +191,8 @@ Important deployment characteristics:
190191
- SQL public network access is enabled.
191192
- Azure services are allowed through the SQL firewall.
192193
- A client-IP firewall rule is added only if `allowedClientIp` is set.
194+
- The frontend and API App Services enable App Service health checks on `/health`.
195+
- The frontend and API apps are instrumented with Azure Monitor OpenTelemetry and receive `APPLICATIONINSIGHTS_CONNECTION_STRING` and `OTEL_SERVICE_NAME` through app settings.
193196
- Linux host auth defaults to `SSH`.
194197
- Key Vault stores `db-password` and `linux-host`.
195198
- The API app receives Key Vault Secrets User access so it can read those secrets at runtime.

0 commit comments

Comments
 (0)