@jaggr2/cdk-cf-dns 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API.md ADDED
@@ -0,0 +1,411 @@
1
+ # API Reference
2
+
3
+ The public API surface of `@jaggr2/cdk-cf-dns`.
4
+
5
+ ## Table of contents
6
+ * **Enums**
7
+ * [CloudflareRecordType](#enumscloudflarerecordtype)
8
+ * **Interfaces**
9
+ * [CloudflareAaaaRecordProps](#interfacescloudflareaaaarecordprops)
10
+ * [CloudflareARecordProps](#interfacescloudflarearecordprops)
11
+ * [CloudflareCaaRecordProps](#interfacescloudflarecaarecordprops)
12
+ * [CloudflareCnameRecordProps](#interfacescloudflarecnamerecordprops)
13
+ * [CloudflareMxRecordProps](#interfacescloudflaremxrecordprops)
14
+ * [CloudflareRecordProps](#interfacescloudflarerecordprops)
15
+ * [CloudflareTxtRecordProps](#interfacescloudflaretxtrecordprops)
16
+ * [CloudflareValidatedCertificateProps](#interfacescloudflarevalidatedcertificateprops)
17
+ * [CloudflareZoneAttributes](#interfacescloudflarezoneattributes)
18
+ * [ICloudflareZone](#interfacesicloudflarezone)
19
+ * **Classes**
20
+ * [CloudflareAaaaRecord](#classescloudflareaaaarecord)
21
+ * [CloudflareARecord](#classescloudflarearecord)
22
+ * [CloudflareCaaRecord](#classescloudflarecaarecord)
23
+ * [CloudflareCertificateProvider](#classescloudflarecertificateprovider)
24
+ * [CloudflareCnameRecord](#classescloudflarecnamerecord)
25
+ * [CloudflareDnsProvider](#classescloudflarednsprovider)
26
+ * [CloudflareMxRecord](#classescloudflaremxrecord)
27
+ * [CloudflareRecord](#classescloudflarerecord)
28
+ * [CloudflareTtl](#classescloudflarettl)
29
+ * [CloudflareTxtRecord](#classescloudflaretxtrecord)
30
+ * [CloudflareValidatedCertificate](#classescloudflarevalidatedcertificate)
31
+ * [CloudflareZone](#classescloudflarezone)
32
+
33
+ ---
34
+
35
+ ## Enums
36
+
37
+ ### CloudflareRecordType
38
+
39
+ The DNS record types supported by this library.
40
+
41
+ | Name | Description |
42
+ |------|-------------|
43
+ | `A` | An IPv4 address record. |
44
+ | `AAAA` | An IPv6 address record. |
45
+ | `CNAME` | A canonical name record. |
46
+ | `TXT` | A text record. |
47
+ | `MX` | A mail exchanger record. |
48
+ | `NS` | A name server record. |
49
+ | `SRV` | A service locator record. |
50
+ | `CAA` | A certification authority authorization record. |
51
+ | `PTR` | A pointer record. |
52
+ | `URI` | A uniform resource identifier record. |
53
+
54
+ ---
55
+
56
+ ## Interfaces
57
+
58
+ ### CloudflareAaaaRecordProps
59
+
60
+ Properties for an AAAA record.
61
+
62
+ Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority">`
63
+
64
+ | Name | Type | Description |
65
+ |------|------|-------------|
66
+ | `content` | `string` | The IPv6 address. |
67
+ | `proxied?` | `boolean` | Whether to proxy the record through Cloudflare. |
68
+
69
+ ### CloudflareARecordProps
70
+
71
+ Properties for an A record.
72
+
73
+ Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority">`
74
+
75
+ | Name | Type | Description |
76
+ |------|------|-------------|
77
+ | `content` | `string` | The IPv4 address. |
78
+ | `proxied?` | `boolean` | Whether to proxy the record through Cloudflare. |
79
+
80
+ ### CloudflareCaaRecordProps
81
+
82
+ Properties for a CAA record.
83
+
84
+ Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority" | "proxied">`
85
+
86
+ | Name | Type | Description |
87
+ |------|------|-------------|
88
+ | `content` | `string` | The CAA value. |
89
+
90
+ ### CloudflareCnameRecordProps
91
+
92
+ Properties for a CNAME record.
93
+
94
+ Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority">`
95
+
96
+ | Name | Type | Description |
97
+ |------|------|-------------|
98
+ | `content` | `string` | The canonical name. |
99
+ | `proxied?` | `boolean` | Whether to proxy the record through Cloudflare. |
100
+
101
+ ### CloudflareMxRecordProps
102
+
103
+ Properties for an MX record.
104
+
105
+ Extends: `Omit<CloudflareRecordProps, "type" | "data" | "proxied">`
106
+
107
+ | Name | Type | Description |
108
+ |------|------|-------------|
109
+ | `content` | `string` | The mail exchanger host. |
110
+ | `priority` | `number` | The MX priority. |
111
+
112
+ ### CloudflareRecordProps
113
+
114
+ Properties for a Cloudflare DNS record.
115
+
116
+ | Name | Type | Description |
117
+ |------|------|-------------|
118
+ | `zone` | `ICloudflareZone` | The Cloudflare zone the record belongs to. |
119
+ | `recordName?` | `string` | Record name. If it does not end in the zone name and `zone.zoneName` is set, it is treated as relative and the zone name is appended. Use `'@'` or omit for the zone apex. |
120
+ | `type` | `CloudflareRecordType` | The record type. |
121
+ | `content?` | `string` | Record value. Mutually exclusive with `data`. |
122
+ | `data?` | `Record<string, unknown>` | Structured value for SRV/CAA/URI records. Mutually exclusive with `content`. |
123
+ | `ttl?` | `cdk.Duration` | The time-to-live for the record. |
124
+ | `proxied?` | `boolean` | Whether to proxy the record through Cloudflare. Only valid for A, AAAA and CNAME. |
125
+ | `priority?` | `number` | Record priority. Required for MX, SRV and URI. |
126
+ | `comment?` | `string` | A free-form comment attached to the record. |
127
+ | `tags?` | `string[]` | Tags attached to the record. |
128
+ | `adoptExisting?` | `boolean` | If a record with the same name+type already exists in Cloudflare, adopt and manage it instead of failing the deployment. |
129
+ | `removalPolicy?` | `cdk.RemovalPolicy` | If RETAIN, the record is left in Cloudflare when the stack resource is deleted. |
130
+
131
+ ### CloudflareTxtRecordProps
132
+
133
+ Properties for a TXT record.
134
+
135
+ Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority" | "proxied">`
136
+
137
+ | Name | Type | Description |
138
+ |------|------|-------------|
139
+ | `content` | `string` | The text value. Values longer than 255 characters are automatically chunked. |
140
+
141
+ ### CloudflareValidatedCertificateProps
142
+
143
+ Properties for a Cloudflare-validated ACM certificate.
144
+
145
+ | Name | Type | Description |
146
+ |------|------|-------------|
147
+ | `domainName` | `string` | The primary domain name the certificate covers. |
148
+ | `subjectAlternativeNames?` | `string[]` | Additional domain names the certificate should cover. |
149
+ | `zone` | `ICloudflareZone` | The Cloudflare zone where the DNS validation CNAMEs are written. |
150
+
151
+ ### CloudflareZoneAttributes
152
+
153
+ Properties for referencing an existing Cloudflare zone.
154
+
155
+ | Name | Type | Description |
156
+ |------|------|-------------|
157
+ | `zoneId` | `string` | The Cloudflare Zone ID, e.g. `"abc123..."`. This may be a plain string or a CDK token (for example resolved from SSM at deploy time). |
158
+ | `apiToken` | `cdk.aws_secretsmanager.ISecret` | The Secrets Manager secret holding the Cloudflare API token. The secret may contain the token as a raw string or as a JSON blob with an `apiToken` key. Only the secret ARN ever appears in the CloudFormation template. |
159
+ | `zoneName?` | `string` | The apex domain, e.g. `"example.com"`. Enables relative record names. |
160
+
161
+ ### ICloudflareZone
162
+
163
+ A reference to a Cloudflare zone that already exists in the Cloudflare account.
164
+
165
+ `CloudflareZone.fromZoneId()` is the only supported way to obtain one; this
166
+ library deliberately does not create zones. Zones are assumed to be managed in
167
+ the Cloudflare dashboard (or elsewhere) and referenced here by their Zone ID.
168
+
169
+ | Name | Type | Description |
170
+ |------|------|-------------|
171
+ | `zoneId` | `string` | The Cloudflare Zone ID, e.g. `"abc123..."`. This may be a plain string or a CDK token (for example resolved from SSM at deploy time). |
172
+ | `zoneName?` | `string` | The apex domain, e.g. `"example.com"`. When set, `recordName` values that do not already end in the zone name are treated as relative and this suffix is appended, matching `aws-cdk-lib/aws-route53` ergonomics. |
173
+ | `apiToken` | `cdk.aws_secretsmanager.ISecret` | The Secrets Manager secret holding the Cloudflare API token. The secret may contain the token as a raw string or as a JSON blob with an `apiToken` key. Only the secret ARN ever appears in the CloudFormation template. |
174
+
175
+ ---
176
+
177
+ ## Classes
178
+
179
+ ### CloudflareAaaaRecord
180
+
181
+ An AAAA record.
182
+
183
+ Extends: `CloudflareRecord`
184
+
185
+
186
+ #### Constructor
187
+
188
+ | Name | Description |
189
+ |------|-------------|
190
+ | `new CloudflareAaaaRecord(scope: Construct, id: string, props: CloudflareAaaaRecordProps)` | |
191
+
192
+ ### CloudflareARecord
193
+
194
+ An A record.
195
+
196
+ Extends: `CloudflareRecord`
197
+
198
+
199
+ #### Constructor
200
+
201
+ | Name | Description |
202
+ |------|-------------|
203
+ | `new CloudflareARecord(scope: Construct, id: string, props: CloudflareARecordProps)` | |
204
+
205
+ ### CloudflareCaaRecord
206
+
207
+ A CAA record.
208
+
209
+ Extends: `CloudflareRecord`
210
+
211
+
212
+ #### Constructor
213
+
214
+ | Name | Description |
215
+ |------|-------------|
216
+ | `new CloudflareCaaRecord(scope: Construct, id: string, props: CloudflareCaaRecordProps)` | |
217
+
218
+ ### CloudflareCertificateProvider
219
+
220
+ The shared custom-resource provider that writes ACM DNS validation records
221
+ into Cloudflare.
222
+
223
+ Like `CloudflareDnsProvider`, one instance exists per stack. It additionally
224
+ grants its handler `acm:DescribeCertificate` on all certificates.
225
+
226
+ Extends: `Construct`
227
+
228
+
229
+ #### Constructor
230
+
231
+ | Name | Description |
232
+ |------|-------------|
233
+ | `new CloudflareCertificateProvider(scope: Construct, id: string)` | *(private)* |
234
+
235
+ #### Properties
236
+
237
+ | Name | Description |
238
+ |------|-------------|
239
+ | `serviceToken: string (readonly)` | The custom-resource service token that the certificate construct uses as its `serviceToken`. |
240
+
241
+ #### Methods
242
+
243
+ | Name | Description |
244
+ |------|-------------|
245
+ | `static getOrCreate(scope: Construct): CloudflareCertificateProvider` | Gets (or lazily creates) the provider for the stack of `scope`. |
246
+ | `grantSecretRead(secret: ISecret): void` | Grants the provider's handler `secretsmanager:GetSecretValue` on the given secret. Repeated grants of the same secret are deduplicated. |
247
+
248
+ ### CloudflareCnameRecord
249
+
250
+ A CNAME record.
251
+
252
+ Extends: `CloudflareRecord`
253
+
254
+
255
+ #### Constructor
256
+
257
+ | Name | Description |
258
+ |------|-------------|
259
+ | `new CloudflareCnameRecord(scope: Construct, id: string, props: CloudflareCnameRecordProps)` | |
260
+
261
+ ### CloudflareDnsProvider
262
+
263
+ The shared custom-resource provider that performs the Cloudflare DNS API
264
+ calls.
265
+
266
+ One instance exists per stack (a singleton keyed by `Stack`), so a stack with
267
+ many records still has exactly one Lambda handler. All records route their
268
+ custom-resource events through this handler.
269
+
270
+ Extends: `Construct`
271
+
272
+
273
+ #### Constructor
274
+
275
+ | Name | Description |
276
+ |------|-------------|
277
+ | `new CloudflareDnsProvider(scope: Construct, id: string)` | *(private)* |
278
+
279
+ #### Properties
280
+
281
+ | Name | Description |
282
+ |------|-------------|
283
+ | `serviceToken: string (readonly)` | The custom-resource service token that records use as their `serviceToken`. |
284
+
285
+ #### Methods
286
+
287
+ | Name | Description |
288
+ |------|-------------|
289
+ | `static getOrCreate(scope: Construct): CloudflareDnsProvider` | Gets (or lazily creates) the provider for the stack of `scope`. |
290
+ | `grantSecretRead(secret: ISecret): void` | Grants the provider's handler `secretsmanager:GetSecretValue` on the given secret. Repeated grants of the same secret are deduplicated. |
291
+
292
+ ### CloudflareMxRecord
293
+
294
+ An MX record.
295
+
296
+ Extends: `CloudflareRecord`
297
+
298
+
299
+ #### Constructor
300
+
301
+ | Name | Description |
302
+ |------|-------------|
303
+ | `new CloudflareMxRecord(scope: Construct, id: string, props: CloudflareMxRecordProps)` | |
304
+
305
+ ### CloudflareRecord
306
+
307
+ A Cloudflare DNS record managed as a CloudFormation resource.
308
+
309
+ The construct synthesises a `Custom::CloudflareDnsRecord` custom resource whose
310
+ Lambda handler calls the Cloudflare API. The API token is resolved at runtime
311
+ from Secrets Manager; only the secret ARN ever appears in the template.
312
+
313
+ Extends: `Construct`
314
+
315
+
316
+ #### Constructor
317
+
318
+ | Name | Description |
319
+ |------|-------------|
320
+ | `new CloudflareRecord(scope: Construct, id: string, props: CloudflareRecordProps)` | |
321
+
322
+ #### Properties
323
+
324
+ | Name | Description |
325
+ |------|-------------|
326
+ | `recordId: string (readonly)` | Cloudflare's record ID, from `GetAtt`. This is the value you can use to locate the record in the Cloudflare dashboard. |
327
+ | `domainName: string (readonly)` | The fully-qualified name actually written to Cloudflare (e.g. `app.example.com`). |
328
+
329
+ ### CloudflareTtl
330
+
331
+ TTL helpers for Cloudflare records.
332
+
333
+
334
+ #### Properties
335
+
336
+ | Name | Description |
337
+ |------|-------------|
338
+ | `static AUTO: cdk.Duration (readonly)` | Cloudflare's automatic TTL (a value of `1` on the wire). |
339
+
340
+ ### CloudflareTxtRecord
341
+
342
+ A TXT record.
343
+
344
+ Extends: `CloudflareRecord`
345
+
346
+
347
+ #### Constructor
348
+
349
+ | Name | Description |
350
+ |------|-------------|
351
+ | `new CloudflareTxtRecord(scope: Construct, id: string, props: CloudflareTxtRecordProps)` | |
352
+
353
+ ### CloudflareValidatedCertificate
354
+
355
+ An ACM certificate whose DNS validation records are written into Cloudflare
356
+ automatically, removing the manual copy-paste step.
357
+
358
+ CloudFormation does not expose the validation `ResourceRecord` as a
359
+ certificate attribute, so a custom resource calls `acm:DescribeCertificate`,
360
+ polls until the records are populated (they are absent for a few seconds
361
+ after creation), and writes each unique CNAME into Cloudflare.
362
+
363
+ Note: if the certificate is attached to CloudFront it must live in
364
+ `us-east-1`; this construct does not solve cross-region certificates.
365
+
366
+ Extends: `Construct`
367
+
368
+
369
+ #### Constructor
370
+
371
+ | Name | Description |
372
+ |------|-------------|
373
+ | `new CloudflareValidatedCertificate(scope: Construct, id: string, props: CloudflareValidatedCertificateProps)` | |
374
+
375
+ #### Properties
376
+
377
+ | Name | Description |
378
+ |------|-------------|
379
+ | `certificate: acm.Certificate (readonly)` | The underlying ACM certificate. |
380
+
381
+ ### CloudflareZone
382
+
383
+ A reference to an existing Cloudflare zone.
384
+
385
+ Zones are not created by this library; they must already exist in the
386
+ Cloudflare account. Use `CloudflareZone.fromZoneId()` to reference one.
387
+
388
+ Extends: `Construct`
389
+
390
+
391
+ #### Constructor
392
+
393
+ | Name | Description |
394
+ |------|-------------|
395
+ | `new CloudflareZone(scope: Construct, id: string, attrs: CloudflareZoneAttributes)` | *(private)* |
396
+
397
+ #### Properties
398
+
399
+ | Name | Description |
400
+ |------|-------------|
401
+ | `zoneId: string (readonly)` | |
402
+ | `zoneName?: string (readonly)` | |
403
+ | `apiToken: cdk.aws_secretsmanager.ISecret (readonly)` | |
404
+
405
+ #### Methods
406
+
407
+ | Name | Description |
408
+ |------|-------------|
409
+ | `static fromZoneId(scope: Construct, id: string, attrs: CloudflareZoneAttributes): ICloudflareZone` | Reference an existing Cloudflare zone by its Zone ID. |
410
+
411
+ *This document is generated by `npm run docs`; do not edit by hand.*
package/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # @jaggr2/cdk-cf-dns
2
+
3
+ Manage Cloudflare DNS records as CloudFormation resources from AWS CDK, so that
4
+ Route 53 hosted zones (and their $0.50/zone/month charge) are no longer needed
5
+ when Cloudflare is already authoritative for the domain.
6
+
7
+ The developer experience is modelled on `aws-cdk-lib/aws-route53`: reference a
8
+ zone, add records, and let the stack manage them. A single shared Lambda custom
9
+ resource performs the Cloudflare API calls — one Lambda per stack, no matter how
10
+ many records you define.
11
+
12
+ ```ts
13
+ const zone = CloudflareZone.fromZoneId(this, 'Zone', {
14
+ zoneId: 'abc123...',
15
+ apiToken: secretsmanager.Secret.fromSecretNameV2(this, 'CfToken', 'cloudflare/dns-token'),
16
+ zoneName: 'example.com',
17
+ });
18
+
19
+ new CloudflareRecord(this, 'AppCname', {
20
+ zone,
21
+ recordName: 'app', // relative -> app.example.com
22
+ type: CloudflareRecordType.CNAME,
23
+ content: distribution.distributionDomainName,
24
+ proxied: true,
25
+ });
26
+ ```
27
+
28
+ ## Install
29
+
30
+ ```
31
+ npm install @jaggr2/cdk-cf-dns
32
+ ```
33
+
34
+ `aws-cdk-lib` and `constructs` are **peer dependencies** — you must have them
35
+ installed in your own project (any CDK v2 app already does):
36
+
37
+ ```
38
+ npm install aws-cdk-lib constructs
39
+ ```
40
+
41
+ You also need **esbuild** as a dev dependency, because the library's Lambda
42
+ handler is bundled with `NodejsFunction` at synthesis time:
43
+
44
+ ```
45
+ npm install --save-dev esbuild
46
+ ```
47
+
48
+ The handler itself has zero runtime npm dependencies — it uses the AWS SDK v3
49
+ that is built into the Lambda runtime and Cloudflare's API over `fetch`.
50
+
51
+ ## Cloudflare API token setup
52
+
53
+ 1. Log in to the Cloudflare dashboard.
54
+ 2. Go to **My Profile → API Tokens → Create Token**.
55
+ 3. Use the **Edit zone DNS** template.
56
+ 4. Scope the token to the single zone you intend to manage (do not select
57
+ "All zones" unless you really need it).
58
+ 5. Create the token, then store it in Secrets Manager so only its ARN ever
59
+ reaches CloudFormation:
60
+
61
+ ```
62
+ aws secretsmanager create-secret --name cloudflare/dns-token --secret-string 'cf_token_here'
63
+ ```
64
+
65
+ The token grants DNS write access to that zone; scope it as narrowly as possible.
66
+ The token itself never appears in your CDK template, an environment variable, or
67
+ a log line — the Lambda resolves it from Secrets Manager at runtime.
68
+
69
+ ## Finding your Zone ID
70
+
71
+ Cloudflare dashboard → select the domain → **Overview** → scroll to the right
72
+ rail → **API** section shows the **Zone ID** (a 32-character hex string).
73
+
74
+ ## Usage
75
+
76
+ ### CNAME to a CloudFront distribution
77
+
78
+ ```ts
79
+ const zone = CloudflareZone.fromZoneId(this, 'Zone', {
80
+ zoneId: 'abc123...',
81
+ zoneName: 'example.com',
82
+ apiToken: secretsmanager.Secret.fromSecretNameV2(this, 'CfToken', 'cloudflare/dns-token'),
83
+ });
84
+
85
+ new CloudflareCnameRecord(this, 'AppCname', {
86
+ zone,
87
+ recordName: 'app', // -> app.example.com
88
+ content: distribution.distributionDomainName,
89
+ proxied: true, // Cloudflare terminates TLS and caches
90
+ });
91
+ ```
92
+
93
+ ### Apex A record
94
+
95
+ ```ts
96
+ new CloudflareARecord(this, 'Apex', {
97
+ zone, // recordName omitted -> zone apex
98
+ content: '1.2.3.4',
99
+ });
100
+ ```
101
+
102
+ ### TXT record for domain verification
103
+
104
+ ```ts
105
+ new CloudflareTxtRecord(this, 'Verify', {
106
+ zone,
107
+ recordName: '_amazonses.example.com',
108
+ content: 'LONG_VERIFICATION_STRING_...', // >255 chars is chunked automatically
109
+ });
110
+ ```
111
+
112
+ ### MX records
113
+
114
+ ```ts
115
+ new CloudflareMxRecord(this, 'Mx', {
116
+ zone,
117
+ content: 'mail.example.com',
118
+ priority: 10,
119
+ });
120
+ ```
121
+
122
+ Every record type also works through the generic `CloudflareRecord` with
123
+ `CloudflareRecordType`, including `SRV`, `CAA` and `URI` (which take a `data`
124
+ object instead of a plain `content` string). Thin subclasses
125
+ (`CloudflareARecord`, `CloudflareAaaaRecord`, `CloudflareCnameRecord`,
126
+ `CloudflareTxtRecord`, `CloudflareMxRecord`, `CloudflareCaaRecord`) narrow the
127
+ type and validate their own required fields.
128
+
129
+ ## Proxied records and ACM
130
+
131
+ If you use ACM certificate DNS validation (for example with CloudFront), the
132
+ `_acme-challenge` CNAMEs generated by `CertificateValidation.fromDns()` **must be
133
+ `proxied: false`** — DNS-01 validation requires the raw CNAME to resolve, and a
134
+ proxied record will never pass. Use a plain CNAME record for validation names:
135
+
136
+ ```ts
137
+ new CloudflareCnameRecord(this, 'AcmeValidation', {
138
+ zone,
139
+ recordName: record.targetName, // e.g. _acme-challenge.example.com
140
+ content: record.domainName, // the validation value
141
+ proxied: false, // CRITICAL
142
+ ttl: cdk.Duration.seconds(60),
143
+ });
144
+ ```
145
+
146
+ ## Automated ACM DNS validation
147
+
148
+ `CloudflareValidatedCertificate` removes the manual copy-paste step during ACM
149
+ DNS validation. It creates the certificate and, via a second shared custom
150
+ resource, polls `acm:DescribeCertificate` until the validation CNAMEs are
151
+ populated (they are absent for a few seconds after creation) and writes each one
152
+ into Cloudflare with `proxied: false` and `ttl: 60`. A wildcard SAN and its apex
153
+ share a single validation record.
154
+
155
+ ```ts
156
+ const cert = new CloudflareValidatedCertificate(this, 'Cert', {
157
+ domainName: 'example.com',
158
+ subjectAlternativeNames: ['*.example.com'],
159
+ zone,
160
+ });
161
+
162
+ new cloudfront.Distribution(this, 'Dist', {
163
+ defaultBehavior: {
164
+ origin: origin,
165
+ viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
166
+ },
167
+ domainNames: ['example.com'],
168
+ certificate: cert.certificate,
169
+ });
170
+ ```
171
+
172
+ The validation records are left in Cloudflare after issuance — they are harmless.
173
+ If the certificate is attached to CloudFront it must live in **us-east-1**.
174
+
175
+ ## Limitations
176
+
177
+ - **No zone creation.** Zones must already exist in Cloudflare; this library only
178
+ references them by Zone ID.
179
+ - **No zone-level settings.** Record-only. SSL/TLS modes, page rules, workers, and
180
+ other zone configuration are out of scope.
181
+ - **No page rules** and **no bulk import**.
182
+ - **Out-of-band edits** to records in the Cloudflare dashboard are only reconciled
183
+ on the next stack update (an update re-applies the desired state; a delete of a
184
+ record that only exists out-of-band is ignored).
185
+ - **Deleting a managed record** requires `adoptExisting: true` if it was created
186
+ outside the stack, otherwise the deployment fails with a helpful error.
187
+
188
+ ## API
189
+
190
+ See [API.md](./API.md) for the generated reference.
191
+
192
+ ## License
193
+
194
+ MIT
@@ -0,0 +1,41 @@
1
+ import * as acm from 'aws-cdk-lib/aws-certificatemanager';
2
+ import { Construct } from 'constructs';
3
+ import { ICloudflareZone } from './zone';
4
+ /**
5
+ * Properties for a Cloudflare-validated ACM certificate.
6
+ */
7
+ export interface CloudflareValidatedCertificateProps {
8
+ /**
9
+ * The primary domain name the certificate covers.
10
+ */
11
+ readonly domainName: string;
12
+ /**
13
+ * Additional domain names the certificate should cover.
14
+ *
15
+ * @default - no additional names
16
+ */
17
+ readonly subjectAlternativeNames?: string[];
18
+ /**
19
+ * The Cloudflare zone where the DNS validation CNAMEs are written.
20
+ */
21
+ readonly zone: ICloudflareZone;
22
+ }
23
+ /**
24
+ * An ACM certificate whose DNS validation records are written into Cloudflare
25
+ * automatically, removing the manual copy-paste step.
26
+ *
27
+ * CloudFormation does not expose the validation `ResourceRecord` as a
28
+ * certificate attribute, so a custom resource calls `acm:DescribeCertificate`,
29
+ * polls until the records are populated (they are absent for a few seconds
30
+ * after creation), and writes each unique CNAME into Cloudflare.
31
+ *
32
+ * Note: if the certificate is attached to CloudFront it must live in
33
+ * `us-east-1`; this construct does not solve cross-region certificates.
34
+ */
35
+ export declare class CloudflareValidatedCertificate extends Construct {
36
+ /**
37
+ * The underlying ACM certificate.
38
+ */
39
+ readonly certificate: acm.Certificate;
40
+ constructor(scope: Construct, id: string, props: CloudflareValidatedCertificateProps);
41
+ }