Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,25 @@ This check is required when **all** of these are true:

## Detection

Search for `AddAzureFunctionsProject` in the AppHost source file(s):
Run the scan script — it finds `*.cs` files that call `AddAzureFunctionsProject`, checks whether each one already sets `AzureWebJobsSecretStorageType`, and prints a single verdict so you don't have to parse raw grep output.

**Bash** — [`scripts/scan-aspire-functions-secrets.sh`](scripts/scan-aspire-functions-secrets.sh):
```bash
grep -rn "AddAzureFunctionsProject" . --include="*.cs"
./scripts/scan-aspire-functions-secrets.sh [directory] # directory defaults to .
```

**PowerShell:**
**PowerShell** — [`scripts/scan-aspire-functions-secrets.ps1`](scripts/scan-aspire-functions-secrets.ps1):
```powershell
Get-ChildItem -Recurse -Filter "*.cs" | Select-String "AddAzureFunctionsProject" -List
.\scripts\scan-aspire-functions-secrets.ps1 [-Path <directory>] # -Path defaults to .
```

If found, check whether `AzureWebJobsSecretStorageType` is already configured in those same file(s):
The script prints one of three verdicts:

```bash
# Check only the AppHost file(s) that contain AddAzureFunctionsProject
find . -name "*.cs" -path "*AppHost*" -print0 | xargs -0 grep -l "AddAzureFunctionsProject" 2>/dev/null | xargs grep -l "AzureWebJobsSecretStorageType"
```

**PowerShell:**
```powershell
Get-ChildItem -Recurse -Filter "*.cs" |
Where-Object { $_.FullName -match "AppHost" } |
Select-String "AddAzureFunctionsProject" -List |
ForEach-Object { Select-String "AzureWebJobsSecretStorageType" -Path $_.Path }
```

**If `AddAzureFunctionsProject` is present but `AzureWebJobsSecretStorageType` is NOT configured in the same file → fix is required.**
| Verdict | Meaning | Action |
|---------|---------|--------|
| `NOT APPLICABLE` | No `AddAzureFunctionsProject` call found | Skip this check |
| `ALREADY CONFIGURED` | Every matching file already sets `AzureWebJobsSecretStorageType` | No change required |
| `FIX REQUIRED` | Matching file(s) call `AddAzureFunctionsProject` but omit the setting (file/line listed) | Apply the **Fix** below to each listed file |

## Fix

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<#
.SYNOPSIS
Aspire + Azure Functions secret-storage pre-provisioning scan.
.DESCRIPTION
Decides whether the AzureWebJobsSecretStorageType=Files fix is required by
scanning C# source for the Aspire Functions builder call and the setting.

Logic:
1. Find *.cs files that call AddAzureFunctionsProject.
2. For each, check whether the same file already sets AzureWebJobsSecretStorageType.
3. Files with the call but missing the setting -> fix required.

Prints a single verdict — NOT APPLICABLE, ALREADY CONFIGURED, or FIX REQUIRED
(with the matching file(s) and line(s)). Exit code is 0 for every verdict;
a non-zero exit only indicates a usage/environment error.
.PARAMETER Path
Directory to scan. Defaults to the current directory.
.EXAMPLE
.\scan-aspire-functions-secrets.ps1
# Scan the current directory
.EXAMPLE
.\scan-aspire-functions-secrets.ps1 -Path ./src
# Scan a specific directory
#>
param(
[string]$Path = "."
)

# Leave $ErrorActionPreference at its default ("Continue") so that Write-Error
# below is non-terminating and the explicit `exit` codes are honored.

if (-not (Test-Path -LiteralPath $Path -PathType Container)) {
Write-Error "'$Path' is not a directory."
exit 2
}

$call = "AddAzureFunctionsProject"
$setting = "AzureWebJobsSecretStorageType"

# Collect *.cs files that reference AddAzureFunctionsProject.
$matches = Get-ChildItem -Path $Path -Recurse -File -Filter "*.cs" |
Where-Object { Select-String -Path $_.FullName -SimpleMatch -Pattern $call -Quiet }

if (-not $matches) {
Write-Output "VERDICT: NOT APPLICABLE"
Write-Output "No '$call' call found in any *.cs file under '$Path'."
Write-Output "The Functions secret-storage check does not apply - skip it."
exit 0
}

# Partition matching files by whether they already configure the setting.
$needsFix = @()
$configured = @()
foreach ($file in $matches) {
if (Select-String -Path $file.FullName -SimpleMatch -Pattern $setting -Quiet) {
$configured += $file
} else {
$needsFix += $file
}
}

if ($needsFix.Count -eq 0) {
Write-Output "VERDICT: ALREADY CONFIGURED"
Write-Output "Every file that calls '$call' already sets '$setting':"
foreach ($file in $configured) {
$line = (Select-String -Path $file.FullName -SimpleMatch -Pattern $setting |
Select-Object -First 1).LineNumber
Write-Output " - $($file.FullName) (line $line)"
}
Write-Output "No change required."
exit 0
}

Write-Output "VERDICT: FIX REQUIRED"
Write-Output "The following file(s) call '$call' but do NOT set '$setting':"
foreach ($file in $needsFix) {
$line = (Select-String -Path $file.FullName -SimpleMatch -Pattern $call |
Select-Object -First 1).LineNumber
Write-Output " - $($file.FullName) (line $line)"
}
Write-Output ""
Write-Output "Add .WithEnvironment(`"$setting`", `"Files`") to the AddAzureFunctionsProject"
Write-Output "builder chain in each file above BEFORE running 'azd provision'."
exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
# scan-aspire-functions-secrets.sh
# Aspire + Azure Functions secret-storage pre-provisioning scan.
#
# Decides whether the AzureWebJobsSecretStorageType=Files fix is required by
# scanning C# source for the Aspire Functions builder call and the setting.
#
# Logic:
# 1. Find *.cs files that call AddAzureFunctionsProject.
# 2. For each, check whether the same file already sets AzureWebJobsSecretStorageType.
# 3. Files with the call but missing the setting -> fix required.
#
# Usage:
# ./scan-aspire-functions-secrets.sh [directory]
#
# Examples:
# ./scan-aspire-functions-secrets.sh # Scan current directory
# ./scan-aspire-functions-secrets.sh ./src # Scan a specific directory
#
# Output: a single verdict — NOT APPLICABLE, ALREADY CONFIGURED, or FIX REQUIRED
# (with the matching file(s) and line(s)). Exit code is 0 for every verdict;
# a non-zero exit only indicates a usage/environment error.

set -euo pipefail

ROOT="${1:-.}"

if [ ! -d "$ROOT" ]; then
echo "ERROR: '$ROOT' is not a directory." >&2
exit 2
fi

CALL="AddAzureFunctionsProject"
SETTING="AzureWebJobsSecretStorageType"

# file_contains <fixed-string> <file>
# Returns 0 if the file contains the literal string, 1 if not.
# A grep read error (exit code 2) is fatal: this is a critical pre-provision
# scan, so a file we cannot read must not be silently reported as "no match".
file_contains() {
local pattern="$1" file="$2" rc
if grep -Fq -- "$pattern" "$file"; then
return 0
fi
rc=$?
if [ "$rc" -eq 2 ]; then
echo "ERROR: failed to read '$file' while scanning for '$pattern'." >&2
exit 3
fi
return 1
}

# first_line <fixed-string> <file>
# Prints the 1-based line number of the first literal match (or nothing).
first_line() {
grep -Fn -- "$1" "$2" | head -n1 | cut -d: -f1
}

# Collect *.cs files that reference AddAzureFunctionsProject into an array.
# find ... -print0 + read -d '' keeps paths intact even if they contain
# newlines or spaces (genuinely NUL-safe, unlike a newline-joined string).
matches=()
while IFS= read -r -d '' file; do
if file_contains "$CALL" "$file"; then
matches+=("$file")
fi
done < <(find "$ROOT" -type f -name "*.cs" -print0)

if [ "${#matches[@]}" -eq 0 ]; then
echo "VERDICT: NOT APPLICABLE"
echo "No '$CALL' call found in any *.cs file under '$ROOT'."
echo "The Functions secret-storage check does not apply — skip it."
exit 0
fi

# Partition matching files by whether they already configure the setting.
needs_fix=()
configured=()
for file in "${matches[@]}"; do
if file_contains "$SETTING" "$file"; then
configured+=("$file")
else
needs_fix+=("$file")
fi
done

if [ "${#needs_fix[@]}" -eq 0 ]; then
echo "VERDICT: ALREADY CONFIGURED"
echo "Every file that calls '$CALL' already sets '$SETTING':"
for file in "${configured[@]}"; do
echo " - $file (line $(first_line "$SETTING" "$file"))"
done
echo "No change required."
exit 0
fi

echo "VERDICT: FIX REQUIRED"
echo "The following file(s) call '$CALL' but do NOT set '$SETTING':"
for file in "${needs_fix[@]}"; do
echo " - $file (line $(first_line "$CALL" "$file"))"
done
echo ""
echo "Add .WithEnvironment(\"$SETTING\", \"Files\") to the AddAzureFunctionsProject"
echo "builder chain in each file above BEFORE running 'azd provision'."
exit 0
Loading