11<#
2- Interactive deployment wizard
2+ . SYNOPSIS
3+ Deploy the WebhookToSplunk app using Azure Bicep for infrastructure provisioning.
4+
5+ . DESCRIPTION
6+ Deploys all Azure infrastructure via azresources.bicep, then builds and
7+ publishes the Function App code. Splunk settings are read from local.settings.json.
8+ Webhook URL secret and HEC token are stored in Key Vault; re-deploying will rotate them
9+ unless you pass -PreserveWebhookSecrets.
10+
11+ . PARAMETER ResourceGroup
12+ string (required) - Resource group to deploy into (must already exist).
13+
14+ . PARAMETER Location
15+ string (default 'northcentralus') - Azure region for all resources.
16+
17+ . PARAMETER ConfigPath
18+ string (default .\DEPLOYMENT_DETAILS\) - path to write log files.
19+
20+ . PARAMETER PreserveWebhookSecrets
21+ switch - if set, reads existing WebhookUrl from Key Vault before deploying
22+ so that existing webhook URLs are not invalidated.
23+
24+ . EXAMPLE
25+ .\Deploy.ps1 -ResourceGroup "netid-w2s"
326#>
27+ [CmdletBinding ()]
28+ param (
29+ [Parameter (Mandatory = $True )]
30+ [string ]$ResourceGroup ,
31+
32+ [Parameter (Mandatory = $False )]
33+ [string ]$Location = ' northcentralus' ,
34+
35+ [Parameter (Mandatory = $False )]
36+ [ValidateScript ({ Test-Path - Path $_ - PathType Container })]
37+ [string ]$ConfigPath = " $PSScriptRoot \DEPLOYMENT_DETAILS" ,
38+
39+ [Parameter (Mandatory = $False )]
40+ [switch ]$PreserveWebhookSecrets = $False
41+ )
42+
43+ Begin {
44+ $LogPath = Join-Path - Path $ConfigPath - ChildPath " Deployment-$ ( Get-Date - Format FileDateTimeUniversal) .log"
45+ Write-Host " Initializing log file at $LogPath "
46+ Try {
47+ Start-Transcript - Path $LogPath - NoClobber - UseMinimalHeader - ErrorAction Stop
48+ Write-Host " Beginning WebhookToSplunk deployment"
49+ }
50+ Catch {
51+ Write-Error " Cannot write to specified ConfigPath: $ConfigPath `n Exiting"
52+ throw $_
53+ }
54+
55+ $null = az account show 2> $null
56+ If ($LASTEXITCODE -ne 0 ) {
57+ Write-Host " Not logged into Azure CLI — launching login"
58+ az login
59+ If ($LASTEXITCODE -ne 0 ) {
60+ Write-Error " Failed to log into Azure"
61+ Stop-Transcript
62+ throw " az login failed"
63+ }
64+ }
65+ $SubscriptionId = az account show -- query id - o tsv
66+ Write-Host " Using subscription: $SubscriptionId "
67+ }
68+
69+ Process {
70+ # Step 1 — Read Splunk settings from local.settings.json
71+ $localCfgPath = Join-Path - Path $PSScriptRoot - ChildPath " local.settings.json"
72+ $cfgTemplatePath = Join-Path - Path $PSScriptRoot - ChildPath " local.settings.template.json"
73+
74+ If (-not (Test-Path - Path $localCfgPath - PathType Leaf)) {
75+ Write-Host " Creating new local.settings.json from template"
76+ Copy-Item - Path $cfgTemplatePath - Destination $localCfgPath
77+ }
78+
79+ $cfg = Get-Content - Path $localCfgPath | ConvertFrom-Json - AsHashtable
80+ $cfgValues = $cfg [' Values' ]
81+ $cfgTemplateValues = (Get-Content - Path $cfgTemplatePath | ConvertFrom-Json - AsHashtable)[' Values' ]
82+
83+ $requiredKeys = $cfgTemplateValues.Keys
84+ $nonCustomKeys = @ (' AzureWebJobsStorage' , ' FUNCTIONS_WORKER_RUNTIME' )
85+ ForEach ($key in $requiredKeys ) {
86+ If ($cfgValues.Keys -notcontains $key -or $cfgValues [$key ] -like " " ) {
87+ If ($nonCustomKeys -contains $key ) {
88+ # skip prompt for these and just use default value
89+ $cfgValues [$key ] = $cfgTemplateValues [$key ]
90+ }
91+ Else {
92+ Write-Error " local.settings.json is missing $key "
93+ [string ]$newVal = Read-Host - Prompt " Enter a value for $key "
94+ $cfgValues [$key ] = $newVal
95+ }
96+ }
97+ Else {
98+ If ($key -eq ' SPLUNK_HEC_TOKEN' ) {
99+ # don't leak hec token in transcript
100+ Write-Host " ✅ SPLUNK_HEC_TOKEN = [redacted]"
101+ }
102+ Else {
103+ Write-Host " ✅ $key = $ ( $cfgValues [$key ]) "
104+ }
105+ }
106+ }
107+ Set-Content - Path $localCfgPath - Value ($cfg | ConvertTo-Json - Depth 5 )
108+ Write-Host " Validated local.settings.json"
109+
110+ # Step 2 — Determine webhook URL secret (generated locally so Key Vault read is not required)
111+ $webhookParams = @ {}
112+ If ($PreserveWebhookSecrets ) {
113+ Write-Host " Attempting to read existing webhook secret from Key Vault..."
114+ Try {
115+ $kvList = az keyvault list -- resource- group $ResourceGroup -- query " [].name" - o tsv
116+ $kvName = $kvList | Select-Object - First 1
117+ If ($kvName ) {
118+ $existingSecret = az keyvault secret show -- vault- name $kvName -- name " WebhookUrl" -- query " value" - o tsv 2> $null
119+ If ($existingSecret ) {
120+ $webhookParams [' webhookUrlSecret' ] = $existingSecret
121+ Write-Host " ✅ Found existing webhook secret — it will be preserved"
122+ }
123+ Else {
124+ Write-Warning " Could not read existing webhook secret from Key Vault; a new one will be generated"
125+ }
126+ }
127+ }
128+ Catch {
129+ Write-Warning " Could not read existing webhook secrets; a new one will be generated"
130+ }
131+ }
132+ If (-not $webhookParams [' webhookUrlSecret' ]) {
133+ $webhookParams [' webhookUrlSecret' ] = [System.Guid ]::NewGuid().ToString()
134+ Write-Host " Generated new webhook URL secret locally"
135+ }
136+ # Keep a reference so the End block can use it without reading from Key Vault
137+ $webhookUrlSecret = $webhookParams [' webhookUrlSecret' ]
138+
139+ # Step 3 — Deploy infrastructure via Bicep
140+ Try {
141+ # check ResourceGroup is valid in this subscription
142+ $rgInfo = (az group show -- name $ResourceGroup ) | ConvertFrom-Json
143+ If ($rgInfo ) {
144+ Write-Host " ✅ Found resource group $ResourceGroup "
145+ }
146+ Else {
147+ throw " Resource group $ResourceGroup does not exist on $SubscriptionId "
148+ }
149+ }
150+ Catch {
151+ Stop-Transcript
152+ throw $_
153+ }
154+ Write-Host " Deploying infrastructure via azresources.bicep..."
155+ $bicepPath = Join-Path - Path $PSScriptRoot - ChildPath " azresources.bicep"
156+
157+ $deployParams = @ (
158+ " --resource-group" , $ResourceGroup ,
159+ " --template-file" , $bicepPath ,
160+ " --parameters" ,
161+ " location=$Location "
162+ )
163+ Foreach ($key in ($cfgValues.Keys | Where-Object - FilterScript { $nonCustomKeys -notcontains $_ })) {
164+ $deployParams += " $key =$ ( $cfgValues [$key ]) "
165+ }
166+ If ($webhookParams [' webhookUrlSecret' ]) {
167+ $deployParams += " webhookUrlSecret=$ ( $webhookParams [' webhookUrlSecret' ]) "
168+ }
169+
170+ # deployment
171+ $deployJson = az deployment group create @deployParams -- query " properties.outputs" - o json
172+ If ($LASTEXITCODE -ne 0 ) {
173+ Write-Error " Bicep deployment failed (exit code $LASTEXITCODE )"
174+ Stop-Transcript
175+ throw " az deployment group create failed"
176+ }
177+ $deployOutput = $deployJson | ConvertFrom-Json
178+
179+ $functionAppName = $deployOutput.functionAppName.value
180+ $kvName = $deployOutput.keyVaultName.value
181+ $hostName = $deployOutput.functionAppHostName.value
182+ Write-Host " ✅ Infrastructure deployed — Function App: $functionAppName , Key Vault: $kvName , Host Name: $hostName "
183+ $deployOutputPath = Join-Path - Path $ConfigPath - ChildPath " AzDeployment.json"
184+ $deployOutput | ConvertTo-Json - Depth 10 | Set-Content - Path $deployOutputPath
185+ Write-Host " Deployment details saved to $deployOutputPath "
186+
187+ # Step 4 — Build TypeScript and publish the function code
188+ Write-Host " Building TypeScript..."
189+ Try {
190+ Push-Location $PSScriptRoot
191+ npm run build -- silent
192+ Write-Host " Publishing function app code..."
193+ func azure functionapp publish $functionAppName -- subscription $SubscriptionId
194+ If ($LASTEXITCODE -ne 0 ) {
195+ throw $LASTEXITCODE
196+ }
197+ Pop-Location
198+ }
199+ Catch {
200+ Pop-Location
201+ Write-Error " Failed to build or publish function app"
202+ Stop-Transcript
203+ throw $_
204+ }
205+ Write-Host " ✅ Function code deployed"
206+ }
207+
208+ End {
209+ Write-Host " "
210+ Write-Host " 🎉 Deployment successful!"
211+ Write-Host " Your webhook endpoint is: https://$hostName /api/webhook?key={secret}"
212+ Write-Host " Webhook secret is stored in Key Vault '$kvName '."
213+ Write-Host " "
214+ Write-Host " ending transcript"
215+ Stop-Transcript
4216
5- # TODO - implement
217+ Write-Warning " Treat your webhook URL like an API key. Do not share it."
218+ $webhookURL = " https://$hostName /api/webhook?key=$webhookUrlSecret "
219+ $secretPath = Join-Path - Path $ConfigPath - ChildPath " SECRET.txt"
220+ Set-Content - Path $secretPath - Value $webhookURL
221+ Write-Host " Webhook URL with secret has been written to $secretPath "
222+ }
0 commit comments