Skip to content

Commit 00504e9

Browse files
authored
Enhancing API Logic Handling, Bug Fixes and Error handling improvements
Added optional reference property to security rules so the extension can track Cloudflare’s ref reliably across deploys. Adjusted security-rule lookup to match on ref and expression first, reducing accidental hits from reused descriptions. Set a custom User-Agent and added retry/backoff for 429/5xx responses to make API calls more resilient. Wrapped JSON deserialization with contextual error messages so malformed API responses are easier to diagnose. Verified DNS deployments now ensure the supplied zoneName now actually matches the resolved zone ID before upsert
1 parent 9762e08 commit 00504e9

File tree

7 files changed

+186
-43
lines changed

7 files changed

+186
-43
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ resource blockCountryTraffic 'SecurityRule' = {
7474
expression: '(ip.src.country eq "CN")'
7575
action: 'block'
7676
enabled: true
77+
reference: 'block-country-cn' // Unique ref
7778
}
7879
7980
output recordName string = txtRecord.name
@@ -83,6 +84,7 @@ output securityRuleId string = blockCountryTraffic.ruleId
8384

8485
> [!NOTE]
8586
> The `SecurityRule` resource maps to the Cloudflare [Security Rules](https://developers.cloudflare.com/security/rules/) API and supports the free plan feature set.
87+
> Specify the optional `reference` property when you need a custom Cloudflare identifier; the extension otherwise defaults it to the resource name on first deploy.
8688
8789
For comprehensive usage examples, please refer to the [`Sample/`](Sample/) directory in this repository.
8890

docs/idempotency.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ The extension keeps repeated `bicep local-deploy` executions safe by reusing exi
99

1010
## Security Rules
1111

12-
- On the first deploy a rule is created with a stable `ref` value matching the resource name, so future runs can find it.
13-
- Each update first attempts a lookup by rule name/description; if that fails, it falls back to downloading a paged rule list for the zone.
12+
- On the first deploy the rule receives a stable `ref` value (by default the resource name, or the optional `reference` property if supplied).
13+
- Each update tries to line up with existing rules by matching `ref` and expression before falling back to names/descriptions; if nothing matches, it downloads a paged rule list for the zone.
1414
- When a matching rule is found, the existing `ruleId` (and filter) are reused and a `PUT` updates the rule. New rules are created with `POST` calls.
1515

1616
Whilst this has a performance hit, it stops subsequent template deployments failing entirely which is a better compromise at this stage.

docs/securityrule.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Manages a Cloudflare Security Rule
66

77
### Block traffic from Country
88

9-
Creates a security rule that blocks requests originating from example Country using the free plan API.
9+
Creates a security rule that blocks requests originating from China using the free plan API.
1010

1111
```bicep
1212
resource blockCountryTraffic 'SecurityRule' = {
@@ -29,5 +29,6 @@ The following arguments are available:
2929
- `zoneId` - (Required) The zone ID this rule applies to
3030
- `description` - (Optional) Human friendly description shown in the Cloudflare dashboard
3131
- `enabled` - (Optional) Whether the rule is enabled (set to false to pause the rule)
32+
- `reference` - (Optional) Reference identifier persisted with the rule; defaults to the resource name on first deploy and cannot be changed by the Cloudflare API afterwards
3233
- `filterId` - (Optional) Cloudflare filter ID associated with this security rule (output only)
3334
- `ruleId` - (Optional) Cloudflare security rule ID (output only)

src/Handlers/CloudFlareDnsRecordHandler.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ protected override async Task<ResourceResponse> CreateOrUpdate(ResourceRequest r
3737
}
3838
}
3939

40+
var normalizedInputZone = request.Properties.ZoneName?.Trim().TrimEnd('.') ?? string.Empty;
41+
if (!string.IsNullOrWhiteSpace(request.Properties.ZoneId))
42+
{
43+
var zone = await apiService.GetZoneAsync(normalizedInputZone, cancellationToken);
44+
if (zone is not null && !string.Equals(zone.Name, normalizedInputZone, StringComparison.OrdinalIgnoreCase))
45+
{
46+
throw new InvalidOperationException($"Zone name '{request.Properties.ZoneName}' does not match the CloudFlare zone '{zone.Name}' (ID {request.Properties.ZoneId}).");
47+
}
48+
}
49+
4050
var createdRecord = await apiService.UpsertDnsRecordAsync(request.Properties, cancellationToken);
4151

4252
// Update properties with the created record data

src/Handlers/CloudFlareSecurityRuleHandler.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ protected override async Task<ResourceResponse> CreateOrUpdate(ResourceRequest r
2323
request.Properties.Description = request.Properties.Name;
2424
}
2525

26+
if (string.IsNullOrWhiteSpace(request.Properties.Reference))
27+
{
28+
request.Properties.Reference = request.Properties.Name.Trim();
29+
}
30+
else
31+
{
32+
request.Properties.Reference = request.Properties.Reference.Trim();
33+
}
34+
2635
var config = Configuration.GetConfiguration();
2736
using var apiService = new CloudFlareApiService(config);
2837

@@ -36,6 +45,10 @@ protected override async Task<ResourceResponse> CreateOrUpdate(ResourceRequest r
3645
{
3746
request.Properties.FilterId = existingRule.Filter.Id;
3847
}
48+
if (!string.IsNullOrWhiteSpace(existingRule.Ref))
49+
{
50+
request.Properties.Reference = existingRule.Ref;
51+
}
3952
}
4053
}
4154

@@ -55,6 +68,7 @@ protected override async Task<ResourceResponse> CreateOrUpdate(ResourceRequest r
5568
request.Properties.Action = updatedRule.Action;
5669
request.Properties.Enabled = updatedRule.Enabled;
5770
request.Properties.Description = updatedRule.Description;
71+
request.Properties.Reference = updatedRule.Reference;
5872

5973
return GetResponse(request);
6074
}

src/Models/CloudFlareModels.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,7 @@ public class CloudFlareSecurityRule : CloudFlareSecurityRuleIdentifiers
200200

201201
[TypeProperty("Cloudflare filter ID associated with this security rule (output only)")]
202202
public string? FilterId { get; set; }
203+
204+
[TypeProperty("Optional reference identifier persisted with the rule; defaults to the resource name on create")]
205+
public string? Reference { get; set; }
203206
}

0 commit comments

Comments
 (0)