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
15 changes: 14 additions & 1 deletion deployers/bicep/postconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,20 @@
item["enable_appinsights_global_logging"] = True

# Scale > Redis Cache
# todo support redis cache configuration
# Only written when this deployment provisioned a cache, so an operator-configured
# external Redis is not overwritten when deployRedisCache is false.
redis_cache_host_name = (var_redisCacheHostName or "").strip()
if redis_cache_host_name:
item["enable_redis_cache"] = True
item["redis_url"] = redis_cache_host_name
item["redis_auth_type"] = var_redisAuthenticationType
# The application uses different service-type identifiers than the Bicep redisCacheKind parameter.
item["redis_service_type"] = (
"azure_managed_redis" if var_redisCacheKind == "managed" else "azure_cache_for_redis"
)
item["redis_port"] = var_redisCachePort
# Empty under managed identity, which also clears a stale key from an earlier key-auth deployment.

Check warning on line 506 in deployers/bicep/postconfig.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
item["redis_key"] = core_service_keys.get("redis_key", "")

# Workspaces > Metadata Extraction
item["enable_extract_meta_data"] = True
Expand Down
2 changes: 1 addition & 1 deletion deployers/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.29
1.0.30
120 changes: 120 additions & 0 deletions functional_tests/test_postconfig_redis_cache_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/env python3
# test_postconfig_redis_cache_configuration.py
"""
Functional test for post-deployment Redis cache configuration.
Version: 0.261.023
Implemented in: 0.261.023

This test ensures the AZD post-deployment configuration script writes the Redis
cache settings that the application reads, so a deployed Azure Managed Redis
instance is actually used instead of silently falling back to filesystem
sessions and in-memory caching.
"""

from pathlib import Path
import re
import sys


REPO_ROOT = Path(__file__).resolve().parents[1]
POSTCONFIG = REPO_ROOT / "deployers" / "bicep" / "postconfig.py"
REDIS_CLIENT = REPO_ROOT / "application" / "single_app" / "functions_redis_client.py"


def require_contains(content: str, expected: str, description: str) -> None:
if expected not in content:
raise AssertionError(f"Missing {description}: {expected}")


def require_not_contains(content: str, unexpected: str, description: str) -> None:
if unexpected in content:
raise AssertionError(f"Unexpected {description}: {unexpected}")


def test_postconfig_writes_redis_cache_settings() -> bool:
print("🧪 Testing postconfig Redis cache configuration")
print("=" * 70)

content = POSTCONFIG.read_text(encoding="utf-8")

require_not_contains(content, "todo support redis cache configuration", "unimplemented Redis configuration")

for key in (
"enable_redis_cache",
"redis_url",
"redis_auth_type",
"redis_service_type",
"redis_port",
"redis_key",
):
require_contains(content, f'item["{key}"]', f"Redis setting assignment for {key}")

require_contains(content, 'if redis_cache_host_name:', "guard so an operator-configured cache is preserved")

print("✅ postconfig writes every Redis setting the application reads")
print("✅ postconfig only overwrites Redis settings when it provisioned a cache")
return True


def test_service_type_identifiers_match_application() -> bool:
"""The deployer must emit the identifiers the application accepts.

The Bicep parameter uses managed/classic while the application uses
azure_managed_redis/azure_cache_for_redis, so a mismatch here would leave the
port and TLS behavior resolving to the wrong Redis offering.
"""
print("\n🧪 Testing Redis service type identifiers match the application")
print("=" * 70)

postconfig = POSTCONFIG.read_text(encoding="utf-8")
client = REDIS_CLIENT.read_text(encoding="utf-8")

supported = dict(
re.findall(
r"^(SERVICE_TYPE_[A-Z_]+)\s*=\s*'([a-z_]+)'",
client,
flags=re.MULTILINE,
)
)
if not supported:
raise AssertionError("Could not read SERVICE_TYPE constants from functions_redis_client.py")

managed = supported.get("SERVICE_TYPE_AZURE_MANAGED_REDIS")
classic = supported.get("SERVICE_TYPE_AZURE_CACHE_FOR_REDIS")
if not managed or not classic:
raise AssertionError(f"Missing expected SERVICE_TYPE constants, found: {sorted(supported)}")

require_contains(postconfig, f'"{managed}"', "managed Redis service type identifier")
require_contains(postconfig, f'"{classic}"', "classic Redis service type identifier")

# The Bicep vocabulary must be translated rather than written through unchanged.
assigned = re.search(

Check warning on line 91 in functional_tests/test_postconfig_redis_cache_configuration.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
r'item\["redis_service_type"\]\s*=\s*\((.*?)\)',
postconfig,
flags=re.DOTALL,
)
if not assigned:
raise AssertionError("Could not locate the redis_service_type assignment")
if '"managed"' not in assigned.group(1):
raise AssertionError("redis_service_type assignment does not branch on the Bicep redisCacheKind value")

print(f"✅ deployer emits '{managed}' and '{classic}'")
print("✅ deployer translates the Bicep redisCacheKind vocabulary")
return True


if __name__ == "__main__":
tests = [
test_postconfig_writes_redis_cache_settings,
test_service_type_identifiers_match_application,
]
results = []
for test in tests:
try:

Check warning on line 113 in functional_tests/test_postconfig_redis_cache_configuration.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
results.append(test())
except Exception as exc:

Check warning on line 115 in functional_tests/test_postconfig_redis_cache_configuration.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
print(f"❌ {test.__name__} failed: {exc}")
results.append(False)

print(f"\n📊 Results: {sum(1 for r in results if r)}/{len(results)} tests passed")

Check warning on line 119 in functional_tests/test_postconfig_redis_cache_configuration.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
sys.exit(0 if all(results) else 1)