octorules-azure
Azure WAF provider for octorules — manages Azure Web Application Firewall custom rules as YAML.
Supports both Azure Front Door WAF (Premium/Standard) and Application Gateway WAF (WAF_v2) through a unified interface. Users write the same YAML regardless of which WAF type is deployed.
Alpha — this provider has comprehensive offline test coverage (698 tests) but has not yet been tested against live Azure WAF APIs. Use with caution and report any issues.
Installation
pip install octorules-azure
This installs octorules (core) and octorules-azure. The provider is auto-discovered — no class: needed in config.
Configuration
# config.yaml
providers:
azure:
subscription_id: env/AZURE_SUBSCRIPTION_ID
resource_group: rg-waf-production
waf_type: front_door # or "app_gateway"
timeout: 30 # API timeout in seconds
max_workers: 1 # parallel workers for multi-zone ops
rules:
directory: ./rules
zones:
apiwafpolicy:
sources:
- rules
websitewafpolicy:
sources:
- rules
Each zone name maps to an Azure WAF policy name. The provider resolves policy names at runtime. Front Door WAF policy names allow only alphanumerics and must start with a letter; Application Gateway policy names also allow hyphens, periods, and underscores. The env/ prefix resolves values from environment variables at runtime. All keys under the provider section are forwarded to the provider constructor as keyword arguments (octodns-style passthrough).
Provider settings
All settings below go under the provider section (e.g. providers.azure).
| Key | Default | Description |
|---|---|---|
subscription_id |
AZURE_SUBSCRIPTION_ID env var |
Azure subscription ID (required) |
resource_group |
AZURE_RESOURCE_GROUP env var |
Resource group containing WAF policies (required) |
waf_type |
front_door / AZURE_WAF_TYPE env var |
"front_door" or "app_gateway" |
timeout |
30 |
API timeout in seconds |
max_workers |
1 |
Parallel workers for multi-zone operations |
Authentication
Uses DefaultAzureCredential — no token is needed in the config file. Common options:
- Service principal (CI/CD):
AZURE_TENANT_ID+AZURE_CLIENT_ID+AZURE_CLIENT_SECRETenv vars - Managed identity (Azure VMs, ACI, App Service): assign identity to the resource, grant WAF Contributor role
- Azure CLI (local development): run
az loginbefore octorules commands - Certificate (automated systems):
AZURE_TENANT_ID+AZURE_CLIENT_ID+AZURE_CLIENT_CERTIFICATE_PATHenv vars
The service principal or managed identity needs the Contributor or Network Contributor role on the resource group containing WAF policies.
Safety thresholds are configured under safety: (framework-owned, not forwarded to the provider):
| Key | Default | Description |
|---|---|---|
safety.delete_threshold |
30.0 |
Max % of rules that can be deleted |
safety.update_threshold |
30.0 |
Max % of rules that can be updated |
safety.min_existing |
3 |
Min rules before thresholds apply |
Examples
The examples/ directory contains a working single-provider
config.yaml plus rules/examplewafpolicy.yaml demonstrating every supported
phase and key. Copy it as a starting point.
Nested format (recommended)
All Azure WAF sections nest under a single azure: block:
# rules/examplewafpolicy.yaml
azure:
waf_custom_rules:
- ref: BlockBadIPs
priority: 1
action: Block
matchConditions:
- matchVariable: RemoteAddr
operator: IPMatch
matchValue:
- 192.168.1.0/24
waf_rate_rules:
- ref: RateLimitAPI
priority: 100
action: Block
ruleType: RateLimitRule
rateLimitDurationInMinutes: 1
rateLimitThreshold: 100
groupBy:
- variableName: SocketAddr
matchConditions:
- matchVariable: RequestUri
operator: BeginsWith
matchValue:
- /api/
waf_managed_rules:
- ref: Microsoft_DefaultRuleSet
ruleSetType: Microsoft_DefaultRuleSet
ruleSetVersion: "2.1"
ruleSetAction: Block
waf_policy_settings:
mode: Prevention
requestBodyCheck: Enabled
waf_managed_exclusions:
- matchVariable: RequestArgNames
selectorMatchOperator: Equals
selector: q
Supported features
| Feature | Status | Azure concept |
|---|---|---|
| Phase rules (3 phases) | Supported | Custom rules, rate limit rules, managed rules |
| Policy settings | Supported | WAF mode, request body inspection, file upload limits |
| Custom rulesets | Not supported | Azure has no separate rule group concept |
| Lists (IP Sets) | Not supported | IPs are inline in matchConditions (up to 600 per condition) |
Zone discovery (list_zones) |
Supported | Lists WAF policies in resource group |
| ETag concurrency control | Supported | Retries on HTTP 412 (concurrent update conflict) |
Phases
| Phase | Rule Type | Description |
|---|---|---|
azure.waf_custom_rules |
MatchRule |
Standard WAF rules (IP blocks, geo-blocks, header checks, etc.) |
azure.waf_rate_rules |
RateLimitRule |
Rate limiting rules with threshold, duration, and grouping |
azure.waf_managed_rules |
Managed | OWASP DRS, Bot Protection rule sets with overrides and exclusions |
Rule format
The same YAML format works for both Front Door and Application Gateway. The adapter translates API-level differences (field naming, SDK models) transparently.
Custom rules
# rules/examplewafpolicy.yaml
azure:
waf_custom_rules:
# Block specific IP ranges
- ref: BlockBadIPs
priority: 1
action: Block
matchConditions:
- matchVariable: RemoteAddr
operator: IPMatch
matchValue:
- 192.168.1.0/24
- 10.0.0.0/8
# Block requests with suspicious user-agent
- ref: BlockBadUserAgents
priority: 2
action: Block
matchConditions:
- matchVariable: RequestHeader
selector: User-Agent
operator: Contains
matchValue:
- evilbot
- scanner
transforms:
- Lowercase
# Geo-block: allow only US and CA
- ref: GeoBlock
priority: 3
action: Block
matchConditions:
- matchVariable: RemoteAddr
operator: GeoMatch
negateCondition: true
matchValue:
- US
- CA
# Block requests matching a regex pattern
- ref: BlockAdminPaths
priority: 4
action: Block
matchConditions:
- matchVariable: RequestUri
operator: RegEx
matchValue:
- "^/admin/.*\\.php$"
transforms:
- Lowercase
Rate-limit rules
azure:
waf_rate_rules:
# Rate limit API endpoints to 100 requests per minute per client IP
- ref: RateLimitAPI
priority: 100
action: Block
ruleType: RateLimitRule
rateLimitDurationInMinutes: 1
rateLimitThreshold: 100
groupBy:
- variableName: SocketAddr
matchConditions:
- matchVariable: RequestUri
operator: BeginsWith
matchValue:
- /api/
Rule fields reference
| Field | Type | Required | Description |
|---|---|---|---|
ref |
string | Yes | Rule name (letters, digits, underscores; max 128 chars) |
priority |
int | Yes | Evaluation order (positive int, lower = first; must be unique across all rules) |
action |
string | Yes | Allow, Block, Log, Redirect, or JSChallenge |
enabledState |
string | No | Enabled (default) or Disabled |
ruleType |
string | No | MatchRule (default) or RateLimitRule |
matchConditions |
list | Yes | One or more conditions (ANDed); max 10 per rule |
rateLimitDurationInMinutes |
int | RateLimitRule | 1 or 5 (time window) |
rateLimitThreshold |
int | RateLimitRule | Requests per client in the window (>= 0; Azure documents no maximum) |
groupBy |
list | No | Rate limit grouping: SocketAddr, GeoLocation, or None |
Match condition fields
| Field | Type | Description |
|---|---|---|
matchVariable |
string | What to match: RemoteAddr, SocketAddr, RequestMethod, QueryString, PostArgs, RequestUri, RequestHeader, RequestBody, Cookies |
selector |
string | Header/cookie/post-arg name (required for RequestHeader, Cookies, PostArgs) |
operator |
string | Any, IPMatch, GeoMatch, Equal, Contains, BeginsWith, EndsWith, RegEx, LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual, ServiceTagMatch |
negateCondition |
bool | Invert the match (default: false) |
matchValue |
list | Values to match against (max 600 for IPMatch, 10 for string operators) |
transforms |
list | Pre-processing: Lowercase, Uppercase, Trim, UrlDecode, UrlEncode, RemoveNulls, HtmlEntityDecode |
WAF Type Differences
The adapter pattern handles all API differences transparently. Users write identical YAML regardless of waf_type:
| Feature | Front Door | Application Gateway |
|---|---|---|
| Scope | Global (edge CDN) | Regional (reverse proxy) |
| Propagation | Up to 45 minutes | Seconds to minutes |
| SDK | azure-mgmt-frontdoor |
azure-mgmt-network |
| Update model | Async LRO (begin_create_or_update) |
Synchronous (create_or_update) |
| Extra actions | Redirect | -- |
| Extra transforms | -- | HtmlEntityDecode |
| Extra operators | ServiceTagMatch | -- |
| Match variable names | RequestHeader, Cookies |
RequestHeaders, RequestCookies (adapter maps) |
| Negation field | negateCondition |
negationConditon (API typo; adapter maps) |
Linting
79 lint rules with AZ prefix covering structure, priority, action, match conditions, rate limits, cross-rule analysis, best practices, and managed rule sets. See docs/lint.md for the full reference with examples.
octorules lint --config config.yaml
Known limitations
- Front Door propagation delay: Configuration changes can take up to 45 minutes to propagate to all edge nodes globally. Plan accordingly for production deployments.
- No PATCH API: Azure WAF uses full-policy PUT. octorules always fetches the current policy first and merges changes, but concurrent external modifications during sync could conflict (mitigated by ETag retry).
- No custom rulesets or lists: Azure WAF has no equivalent of AWS Rule Groups or IP Sets. IP addresses are inline in
matchConditions(max 600 per condition, 60,000 across all rules). - Regex limit: Maximum 5 custom rules using the
RegExoperator per policy. - Priority must be globally unique: Rule priorities must be unique across ALL custom rules in a policy (both MatchRule and RateLimitRule combined), not just within a phase.
Development
python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest tests/ -v
.venv/bin/ruff check octorules_azure/ tests/
.venv/bin/ruff format --check octorules_azure/ tests/
Pre-commit hook:
pre-commit install
License
octorules-azure is licensed under the Apache License 2.0.
Metadata
Release files for octorules-azure 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| octorules_azure-0.6.0.tar.gz | 83.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| octorules_azure-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 131.1 kB
Release files / octorules_azure-0.6.0.tar.gz
| Download URL | octorules_azure-0.6.0.tar.gz |
|---|---|
| Size | 83.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
43053413ac93ffbf538dc10ce5dfaf44c5dccaf9f3c4f2e43d3b29cb295a59a2
|
|
BLAKE2b-256 checksum How to use checksums |
ba56cd331c3c78c8f383b80bdca08afdd47452c8c1274bf07eb1640fde2115d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.
Transparency logRelease files / octorules_azure-0.6.0-py3-none-any.whl
| Download URL | octorules_azure-0.6.0-py3-none-any.whl |
|---|---|
| Size | 47.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aa106a73483540f4760c05c40a3ac997db577dc5545dff2100262a30540f2636
|
|
BLAKE2b-256 checksum How to use checksums |
cffca571ba7473837aee2f0dacae335c224e00becdc6aa05819cc154a4d3033f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.
Transparency logSecurity for octorules-azure 0.6.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for octorules_azure-0.6.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.