1use crate::cloudfront::{CacheBehavior, CachePolicy, CachePolicyConfig, CachePolicyProperties, CachePolicyRef, CachePolicyType, CookiesConfig, CustomOriginConfig, DefaultCacheBehavior, Distribution, DistributionConfig, DistributionProperties, DistributionRef, DistributionType, HeadersConfig, Origin, OriginAccessControl, OriginAccessControlConfig, OriginAccessControlDtoType, OriginAccessControlRef, OriginControlProperties, OriginCustomHeader, ParametersInCacheKeyAndForwardedToOrigin, QueryStringsConfig, S3OriginConfig, ViewerCertificate, VpcOriginConfig};
2use crate::iam::{Effect, PolicyDocumentBuilder, PrincipalBuilder, StatementBuilder};
3use crate::intrinsic::{get_att, get_ref, join, AWS_ACCOUNT_PSEUDO_PARAM};
4use crate::s3::BucketPolicyBuilder;
5use crate::s3::BucketRef;
6use crate::shared::HttpMethod::{Delete, Get, Head, Options, Patch, Post, Put};
7use crate::shared::Id;
8use crate::stack::{Resource, StackBuilder};
9use crate::wrappers::{CfConnectionTimeout, ConnectionAttempts, DefaultRootObject, IamAction, OriginPath, S3OriginReadTimeout};
10use serde_json::{json, Value};
11use std::marker::PhantomData;
12use crate::type_state;
13
14#[derive(Debug, Clone)]
15pub enum SslSupportedMethod {
16 SniOnly,
17 Vip,
18 StaticIp,
19}
20
21impl From<SslSupportedMethod> for String {
22 fn from(value: SslSupportedMethod) -> Self {
23 match value {
24 SslSupportedMethod::SniOnly => "sni-only".to_string(),
25 SslSupportedMethod::Vip => "vip".to_string(),
26 SslSupportedMethod::StaticIp => "static-ip".to_string(),
27 }
28 }
29}
30
31#[derive(Debug, Clone)]
32pub enum MinProtocolVersion {
33 SSLV3,
34 TLSv1,
35 TLSv1_1,
36 TLSv1_2_2018,
37 TLSv1_2_2019,
38 TLSv1_2_2021,
39 TLSv1_2_2025,
40 TLSv1_3,
41}
42
43impl From<MinProtocolVersion> for String {
44 fn from(value: MinProtocolVersion) -> Self {
45 match value {
46 MinProtocolVersion::SSLV3 => "SSLv3".to_string(),
47 MinProtocolVersion::TLSv1 => "TLSv1".to_string(),
48 MinProtocolVersion::TLSv1_1 => "TLSv1.1_2016".to_string(),
49 MinProtocolVersion::TLSv1_2_2018 => "TLSv1.2_2018".to_string(),
50 MinProtocolVersion::TLSv1_2_2019 => "TLSv1.2_2019".to_string(),
51 MinProtocolVersion::TLSv1_2_2021 => "TLSv1.2_2021".to_string(),
52 MinProtocolVersion::TLSv1_2_2025 => "TLSv1.2_2025".to_string(),
53 MinProtocolVersion::TLSv1_3 => "TLSv1.3_2025".to_string(),
54 }
55 }
56}
57
58type_state!(
59 ViewerCertificateState,
60 ViewerCertificateStateStartState,
61 ViewerCertificateStateAcmOrIamState,
62 ViewerCertificateStateEndState,
63);
64
65pub struct ViewerCertificateBuilder<T: ViewerCertificateState> {
67 phantom_data: PhantomData<T>,
68 cloudfront_default_cert: Option<bool>,
69 acm_cert_arn: Option<String>,
70 iam_cert_id: Option<String>,
71 min_protocol_version: Option<String>,
72 ssl_support_method: Option<String>,
73}
74
75impl Default for ViewerCertificateBuilder<ViewerCertificateStateStartState> {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81impl ViewerCertificateBuilder<ViewerCertificateStateStartState> {
82 pub fn new() -> ViewerCertificateBuilder<ViewerCertificateStateStartState> {
83 ViewerCertificateBuilder {
84 phantom_data: Default::default(),
85 acm_cert_arn: None,
86 cloudfront_default_cert: None,
87 iam_cert_id: None,
88 min_protocol_version: None,
89 ssl_support_method: None,
90 }
91 }
92
93 pub fn cloudfront_default_cert(self) -> ViewerCertificateBuilder<ViewerCertificateStateEndState> {
94 ViewerCertificateBuilder {
95 phantom_data: Default::default(),
96 cloudfront_default_cert: Some(true),
97 acm_cert_arn: self.acm_cert_arn,
98 iam_cert_id: self.iam_cert_id,
99 min_protocol_version: self.min_protocol_version,
100 ssl_support_method: self.ssl_support_method,
101 }
102 }
103
104 pub fn iam_cert_id(self, id: String) -> ViewerCertificateBuilder<ViewerCertificateStateAcmOrIamState> {
105 ViewerCertificateBuilder {
106 phantom_data: Default::default(),
107 cloudfront_default_cert: Some(true),
108 acm_cert_arn: self.acm_cert_arn,
109 iam_cert_id: Some(id),
110 min_protocol_version: self.min_protocol_version,
111 ssl_support_method: self.ssl_support_method,
112 }
113 }
114
115 pub fn acm_cert_arn(self, id: String) -> ViewerCertificateBuilder<ViewerCertificateStateAcmOrIamState> {
116 ViewerCertificateBuilder {
117 phantom_data: Default::default(),
118 cloudfront_default_cert: Some(true),
119 acm_cert_arn: Some(id),
120 iam_cert_id: self.iam_cert_id,
121 min_protocol_version: self.min_protocol_version,
122 ssl_support_method: self.ssl_support_method,
123 }
124 }
125}
126
127impl<T: ViewerCertificateState> ViewerCertificateBuilder<T> {
128 fn build_internal(self) -> ViewerCertificate {
129 ViewerCertificate {
130 cloudfront_default_cert: self.cloudfront_default_cert,
131 acm_cert_arn: self.acm_cert_arn,
132 iam_cert_id: self.iam_cert_id,
133 min_protocol_version: self.min_protocol_version,
134 ssl_support_method: self.ssl_support_method,
135 }
136 }
137}
138
139impl ViewerCertificateBuilder<ViewerCertificateStateAcmOrIamState> {
140 pub fn min_protocol_version(self, protocol_version: MinProtocolVersion) -> Self {
141 Self {
142 phantom_data: Default::default(),
143 min_protocol_version: Some(protocol_version.into()),
144 cloudfront_default_cert: self.cloudfront_default_cert,
145 acm_cert_arn: self.acm_cert_arn,
146 iam_cert_id: self.iam_cert_id,
147 ssl_support_method: self.ssl_support_method,
148 }
149 }
150
151 pub fn ssl_support_method(self, ssl_support: SslSupportedMethod) -> Self {
152 Self {
153 phantom_data: Default::default(),
154 ssl_support_method: Some(ssl_support.into()),
155 min_protocol_version: self.min_protocol_version,
156 cloudfront_default_cert: self.cloudfront_default_cert,
157 acm_cert_arn: self.acm_cert_arn,
158 iam_cert_id: self.iam_cert_id,
159 }
160 }
161
162 #[must_use]
163 pub fn build(self) -> ViewerCertificate {
164 self.build_internal()
165 }
166}
167
168impl ViewerCertificateBuilder<ViewerCertificateStateEndState> {
169 #[must_use]
170 pub fn build(self) -> ViewerCertificate {
171 self.build_internal()
172 }
173}
174
175#[derive(Debug, Clone)]
176pub enum Cookies {
177 None,
178 Whitelist(Vec<String>),
179 AllExcept(Vec<String>),
180 All,
181}
182
183#[derive(Debug, Clone)]
184pub enum QueryString {
185 None,
186 Whitelist(Vec<String>),
187 AllExcept(Vec<String>),
188 All,
189}
190
191#[derive(Debug, Clone)]
192pub enum Headers {
193 None,
194 Whitelist(Vec<String>),
195}
196
197pub struct ParametersInCacheKeyAndForwardedToOriginBuilder {
201 cookies_config: CookiesConfig,
202 headers_config: HeadersConfig,
203 query_strings_config: QueryStringsConfig,
204 accept_encoding_gzip: bool,
205 accept_encoding_brotli: Option<bool>,
206}
207
208impl ParametersInCacheKeyAndForwardedToOriginBuilder {
209 pub fn new(accept_encoding_gzip: bool, cookies: Cookies, query_string: QueryString, headers: Headers) -> Self {
210 let cookies_config = match cookies {
211 Cookies::None => CookiesConfig {
212 cookie_behavior: "none".to_string(),
213 cookies: None,
214 },
215 Cookies::Whitelist(list) => CookiesConfig {
216 cookie_behavior: "whitelist".to_string(),
217 cookies: Some(list),
218 },
219 Cookies::AllExcept(list) => CookiesConfig {
220 cookie_behavior: "allExcept".to_string(),
221 cookies: Some(list),
222 },
223 Cookies::All => CookiesConfig {
224 cookie_behavior: "all".to_string(),
225 cookies: None,
226 },
227 };
228 let query_strings_config = match query_string {
229 QueryString::None => QueryStringsConfig {
230 query_strings_behavior: "none".to_string(),
231 query_strings: None,
232 },
233 QueryString::Whitelist(list) => QueryStringsConfig {
234 query_strings_behavior: "whitelist".to_string(),
235 query_strings: Some(list),
236 },
237 QueryString::AllExcept(list) => QueryStringsConfig {
238 query_strings_behavior: "allExcept".to_string(),
239 query_strings: Some(list),
240 },
241 QueryString::All => QueryStringsConfig {
242 query_strings_behavior: "all".to_string(),
243 query_strings: None,
244 },
245 };
246 let headers_config = match headers {
247 Headers::None => HeadersConfig {
248 headers_behavior: "none".to_string(),
249 headers: None,
250 },
251 Headers::Whitelist(list) => HeadersConfig {
252 headers_behavior: "whitelist".to_string(),
253 headers: Some(list),
254 },
255 };
256
257 Self {
258 cookies_config,
259 headers_config,
260 query_strings_config,
261 accept_encoding_gzip,
262 accept_encoding_brotli: None,
263 }
264 }
265
266 pub fn accept_encoding_brotli(self, accept: bool) -> Self {
267 Self {
268 accept_encoding_brotli: Some(accept),
269 ..self
270 }
271 }
272
273 #[must_use]
274 pub fn build(self) -> ParametersInCacheKeyAndForwardedToOrigin {
275 ParametersInCacheKeyAndForwardedToOrigin {
276 cookies_config: self.cookies_config,
277 accept_encoding_brotli: self.accept_encoding_brotli,
278 accept_encoding_gzip: self.accept_encoding_gzip,
279 headers_config: self.headers_config,
280 query_strings_config: self.query_strings_config,
281 }
282 }
283}
284
285pub struct CachePolicyBuilder {
287 id: Id,
288 name: String,
289 default_ttl: u32,
290 min_ttl: u32,
291 max_ttl: u32,
292 cache_params: ParametersInCacheKeyAndForwardedToOrigin,
293}
294
295impl CachePolicyBuilder {
296 pub fn new(
306 id: &str,
307 unique_name: &str,
308 default_ttl: u32,
309 min_ttl: u32,
310 max_ttl: u32,
311 cache_params: ParametersInCacheKeyAndForwardedToOrigin,
312 ) -> Self {
313 Self {
314 id: Id(id.to_string()),
315 name: unique_name.to_string(),
316 default_ttl,
317 min_ttl,
318 max_ttl,
319 cache_params,
320 }
321 }
322
323 pub fn build(self, stack_builder: &mut StackBuilder) -> CachePolicyRef {
324 let resource_id = Resource::generate_id("CachePolicy");
325 stack_builder.add_resource(CachePolicy {
326 id: self.id,
327 resource_id: resource_id.clone(),
328 r#type: CachePolicyType::CachePolicyType,
329 properties: CachePolicyProperties {
330 config: CachePolicyConfig {
331 default_ttl: self.default_ttl,
332 min_ttl: self.min_ttl,
333 max_ttl: self.max_ttl,
334 name: self.name,
335 params_in_cache_key_and_forwarded: self.cache_params,
336 },
337 },
338 });
339 CachePolicyRef::internal_new(resource_id)
340 }
341}
342
343#[derive(Debug, Clone)]
344pub enum HttpVersion {
345 Http1,
346 Http2,
347 Http3,
348 Http2And3,
349}
350
351impl From<HttpVersion> for String {
352 fn from(value: HttpVersion) -> Self {
353 match value {
354 HttpVersion::Http1 => "http1.1".to_string(),
355 HttpVersion::Http2 => "http2".to_string(),
356 HttpVersion::Http3 => "http3".to_string(),
357 HttpVersion::Http2And3 => "http2and3".to_string(),
358 }
359 }
360}
361
362#[derive(Debug, Clone)]
363pub enum PriceClass {
364 PriceClass100,
365 PriceClass200,
366 PriceClassAll,
367 None,
368}
369
370impl From<PriceClass> for String {
371 fn from(value: PriceClass) -> Self {
372 match value {
373 PriceClass::PriceClass100 => "PriceClass_100".to_string(),
374 PriceClass::PriceClass200 => "PriceClass_200".to_string(),
375 PriceClass::PriceClassAll => "PriceClass_All".to_string(),
376 PriceClass::None => "None".to_string(),
377 }
378 }
379}
380
381#[derive(Debug, Clone)]
382pub enum OriginProtocolPolicy {
383 HttpOnly,
384 MatchViewer,
385 HttpsOnly,
386}
387
388impl From<OriginProtocolPolicy> for String {
389 fn from(value: OriginProtocolPolicy) -> Self {
390 match value {
391 OriginProtocolPolicy::HttpOnly => "http-only".to_string(),
392 OriginProtocolPolicy::MatchViewer => "match-viewer".to_string(),
393 OriginProtocolPolicy::HttpsOnly => "https-only".to_string(),
394 }
395 }
396}
397
398#[derive(Debug, Clone)]
399pub enum IpAddressType {
400 IPv4,
401 IPv6,
402 Dualstack,
403}
404
405impl From<IpAddressType> for String {
406 fn from(value: IpAddressType) -> Self {
407 match value {
408 IpAddressType::IPv4 => "ipv4".to_string(),
409 IpAddressType::IPv6 => "ipv6".to_string(),
410 IpAddressType::Dualstack => "dualstack".to_string(),
411 }
412 }
413}
414
415type_state!(
416 OriginState,
417 OriginStartState,
418 OriginS3OriginState,
419 OriginCustomOriginState,
420);
421
422pub struct OriginBuilder<T: OriginState> {
426 phantom_data: PhantomData<T>,
427 id: String,
428 bucket_arn: Option<Value>,
429 bucket_ref: Option<Value>,
430 domain_name: Option<Value>,
431 connection_attempts: Option<u8>,
432 connection_timeout: Option<u16>,
433 response_completion_timeout: Option<u16>,
434 origin_access_control_id: Option<Value>,
435 origin_path: Option<String>,
436 s3origin_config: Option<S3OriginConfig>,
437 origin_custom_headers: Option<Vec<OriginCustomHeader>>,
438 vpc_origin_config: Option<VpcOriginConfig>,
439 custom_origin_config: Option<CustomOriginConfig>,
440}
441
442impl OriginBuilder<OriginStartState> {
443 pub fn new(origin_id: &str) -> Self {
444 Self {
445 phantom_data: Default::default(),
446 id: origin_id.to_string(),
447 bucket_arn: None,
448 bucket_ref: None,
449 domain_name: None,
450 connection_attempts: None,
451 connection_timeout: None,
452 origin_access_control_id: None,
453 origin_path: None,
454 response_completion_timeout: None,
455 s3origin_config: None,
456 origin_custom_headers: None,
457 vpc_origin_config: None,
458 custom_origin_config: None,
459 }
460 }
461
462 pub fn s3_origin(
466 self,
467 bucket: &BucketRef,
468 oac: &OriginAccessControlRef,
469 origin_read_timeout: Option<S3OriginReadTimeout>,
470 ) -> OriginBuilder<OriginS3OriginState> {
471 let s3origin_config = S3OriginConfig {
472 origin_read_timeout: origin_read_timeout.map(|v| v.0),
473 };
474
475 let domain = bucket.get_att("RegionalDomainName");
476
477 OriginBuilder {
478 phantom_data: Default::default(),
479 id: self.id.to_string(),
480 bucket_arn: Some(bucket.get_arn()),
481 bucket_ref: Some(bucket.get_ref()),
482 domain_name: Some(domain),
483 connection_attempts: self.connection_attempts,
484 connection_timeout: self.connection_timeout,
485 origin_access_control_id: Some(oac.get_att("Id")),
486 origin_path: self.origin_path,
487 response_completion_timeout: self.response_completion_timeout,
488 origin_custom_headers: self.origin_custom_headers,
489 s3origin_config: Some(s3origin_config),
490 vpc_origin_config: None,
491 custom_origin_config: None,
492 }
493 }
494
495 pub fn custom_origin(self, domain: &str, policy: OriginProtocolPolicy) -> OriginBuilder<OriginCustomOriginState> {
500 let custom_origin_config = CustomOriginConfig {
501 origin_protocol_policy: policy.into(),
502 http_port: None,
503 https_port: None,
504 ip_address_type: None,
505 origin_keep_alive_timeout: None,
506 origin_read_timeout: None,
507 origin_ssl_protocols: None,
508 };
509
510 OriginBuilder {
511 phantom_data: Default::default(),
512 id: self.id.to_string(),
513 domain_name: Some(Value::String(domain.to_string())),
514 connection_attempts: self.connection_attempts,
515 connection_timeout: self.connection_timeout,
516 origin_path: self.origin_path,
517 response_completion_timeout: self.response_completion_timeout,
518 origin_custom_headers: self.origin_custom_headers,
519 custom_origin_config: Some(custom_origin_config),
520 vpc_origin_config: None,
521 origin_access_control_id: None,
522 s3origin_config: None,
523 bucket_arn: None,
524 bucket_ref: None,
525 }
526 }
527}
528
529impl<T: OriginState> OriginBuilder<T> {
530 pub fn connection_attempts(self, attempts: ConnectionAttempts) -> Self {
531 Self {
532 connection_attempts: Some(attempts.0),
533 ..self
534 }
535 }
536
537 pub fn timeouts(self, timeouts: CfConnectionTimeout) -> Self {
538 Self {
539 connection_timeout: timeouts.0,
540 response_completion_timeout: timeouts.1,
541 ..self
542 }
543 }
544
545 pub fn origin_path(self, path: OriginPath) -> Self {
546 Self {
547 origin_path: Some(path.0),
548 ..self
549 }
550 }
551
552 fn build_internal(self) -> Origin {
553 Origin {
554 id: self.id,
555 s3_bucket_policy: None,
556 domain_name: self.domain_name.expect("domain name should be present for cloudfront distribution"),
557 connection_attempts: self.connection_attempts,
558 connection_timeout: self.connection_timeout,
559 origin_access_control_id: self.origin_access_control_id,
560 origin_path: self.origin_path,
561 response_completion_timeout: self.response_completion_timeout,
562 s3origin_config: self.s3origin_config,
563 origin_custom_headers: self.origin_custom_headers,
564 vpc_origin_config: self.vpc_origin_config,
565 custom_origin_config: self.custom_origin_config,
566 }
567 }
568}
569
570impl OriginBuilder<OriginCustomOriginState> {
571 pub fn ip_address_type(self, address_type: IpAddressType) -> Self {
572 let mut config = self.custom_origin_config.expect("custom config to be present in Custom Origin State");
573 config.ip_address_type = Some(address_type.into());
574
575 OriginBuilder {
576 custom_origin_config: Some(config),
577 ..self
578 }
579 }
580 pub fn http_port(self, port: u16) -> Self {
581 let mut config = self.custom_origin_config.expect("custom config to be present in Custom Origin State");
582 config.http_port = Some(port);
583
584 OriginBuilder {
585 custom_origin_config: Some(config),
586 ..self
587 }
588 }
589
590 pub fn https_port(self, port: u16) -> Self {
591 let mut config = self.custom_origin_config.expect("custom config to be present in Custom Origin State");
592 config.https_port = Some(port);
593
594 OriginBuilder {
595 custom_origin_config: Some(config),
596 ..self
597 }
598 }
599
600 pub fn origin_keep_alive_timeout(self, timeout: u8) -> Self {
601 let mut config = self.custom_origin_config.expect("custom config to be present in Custom Origin State");
602 config.origin_keep_alive_timeout = Some(timeout);
603
604 OriginBuilder {
605 custom_origin_config: Some(config),
606 ..self
607 }
608 }
609
610 pub fn origin_read_timeout(self, timeout: u8) -> Self {
611 let mut config = self.custom_origin_config.expect("custom config to be present in Custom Origin State");
612 config.origin_read_timeout = Some(timeout);
613
614 OriginBuilder {
615 custom_origin_config: Some(config),
616 ..self
617 }
618 }
619
620 pub fn add_origin_ssl_protocol(self, protocol: String) -> Self {
621 let mut config = self.custom_origin_config.expect("custom config to be present in Custom Origin State");
622
623 let protocols = if let Some(mut protocols) = config.origin_ssl_protocols {
624 protocols.push(protocol);
625 protocols
626 } else {
627 vec![protocol]
628 };
629
630 config.origin_ssl_protocols = Some(protocols);
631
632 OriginBuilder {
633 custom_origin_config: Some(config),
634 ..self
635 }
636 }
637
638 pub fn build(self) -> Origin {
639 self.build_internal()
640 }
641}
642
643impl OriginBuilder<OriginS3OriginState> {
644 pub fn build(mut self) -> Origin {
645 let bucket_ref = self.bucket_ref.take().expect("bucket ref to be present in S3 origin state");
646 let bucket_arn = self.bucket_arn.take().expect("bucket arn to be present in S3 origin state");
647
648 let bucket_items = vec![join("", vec![bucket_arn, Value::String("/*".to_string())])];
649 let principal = PrincipalBuilder::new().service("cloudfront.amazonaws.com".to_string()).build();
650 let statement = StatementBuilder::new(vec![IamAction("s3:GetObject".to_string())], Effect::Allow)
651 .resources(bucket_items)
652 .principal(principal)
653 .build();
654 let doc = PolicyDocumentBuilder::new(vec![statement]).build();
655 let bucket_policy_id = format!("{}-website-s3-policy", self.id);
656 let (_, s3_policy) = BucketPolicyBuilder::internal_new(bucket_policy_id.as_str(), bucket_ref, doc).raw_build();
657
658 let mut origin = self.build_internal();
659 origin.s3_bucket_policy = Some(s3_policy);
660
661 origin
662 }
663}
664
665#[derive(Debug, Clone)]
666pub enum DefaultCacheAllowedMethods {
667 GetHead,
668 GetHeadOptions,
669 All,
670}
671
672impl From<DefaultCacheAllowedMethods> for Vec<String> {
673 fn from(value: DefaultCacheAllowedMethods) -> Self {
674 match value {
675 DefaultCacheAllowedMethods::GetHead => vec![Get.into(), Head.into()],
676 DefaultCacheAllowedMethods::GetHeadOptions => vec![Get.into(), Head.into(), Options.into()],
677 DefaultCacheAllowedMethods::All => vec![
678 Get.into(),
679 Head.into(),
680 Options.into(),
681 Put.into(),
682 Patch.into(),
683 Post.into(),
684 Delete.into(),
685 ],
686 }
687 }
688}
689
690#[derive(Debug, Clone)]
691pub enum DefaultCacheCachedMethods {
692 GetHead,
693 GetHeadOptions,
694}
695
696impl From<DefaultCacheCachedMethods> for Vec<String> {
697 fn from(value: DefaultCacheCachedMethods) -> Self {
698 match value {
699 DefaultCacheCachedMethods::GetHead => vec![Get.into(), Head.into()],
700 DefaultCacheCachedMethods::GetHeadOptions => vec![Get.into(), Head.into(), Options.into()],
701 }
702 }
703}
704
705#[derive(Debug, Clone)]
706pub enum ViewerProtocolPolicy {
707 AllowAll,
708 RedirectToHttps,
709 HttpsOnly,
710}
711
712impl From<ViewerProtocolPolicy> for String {
713 fn from(value: ViewerProtocolPolicy) -> Self {
714 match value {
715 ViewerProtocolPolicy::AllowAll => "allow-all".to_string(),
716 ViewerProtocolPolicy::RedirectToHttps => "redirect-to-https".to_string(),
717 ViewerProtocolPolicy::HttpsOnly => "https-only".to_string(),
718 }
719 }
720}
721
722pub struct DefaultCacheBehaviorBuilder {
724 target_origin_id: String,
725 cache_policy_id: Value,
726 viewer_protocol_policy: String,
727 allowed_methods: Option<Vec<String>>,
728 cached_methods: Option<Vec<String>>,
729 compress: Option<bool>,
730}
731
732impl DefaultCacheBehaviorBuilder {
733 pub fn new(origin: &Origin, policy: &CachePolicyRef, viewer_protocol_policy: ViewerProtocolPolicy) -> Self {
734 Self {
735 target_origin_id: origin.get_origin_id().to_string(),
736 cache_policy_id: policy.get_att("Id"),
737 viewer_protocol_policy: viewer_protocol_policy.into(),
738 allowed_methods: None,
739 cached_methods: None,
740 compress: None,
741 }
742 }
743
744 pub fn allowed_methods(self, methods: DefaultCacheAllowedMethods) -> Self {
745 Self {
746 allowed_methods: Some(methods.into()),
747 target_origin_id: self.target_origin_id,
748 cache_policy_id: self.cache_policy_id,
749 viewer_protocol_policy: self.viewer_protocol_policy,
750 cached_methods: self.cached_methods,
751 compress: self.compress,
752 }
753 }
754
755 pub fn cached_methods(self, methods: DefaultCacheCachedMethods) -> Self {
756 Self {
757 cached_methods: Some(methods.into()),
758 target_origin_id: self.target_origin_id,
759 cache_policy_id: self.cache_policy_id,
760 viewer_protocol_policy: self.viewer_protocol_policy,
761 allowed_methods: self.allowed_methods,
762 compress: self.compress,
763 }
764 }
765
766 pub fn compress(self, compress: bool) -> Self {
767 Self {
768 compress: Some(compress),
769 target_origin_id: self.target_origin_id,
770 cache_policy_id: self.cache_policy_id,
771 viewer_protocol_policy: self.viewer_protocol_policy,
772 allowed_methods: self.allowed_methods,
773 cached_methods: self.cached_methods,
774 }
775 }
776
777 pub fn build(self) -> DefaultCacheBehavior {
778 DefaultCacheBehavior {
779 target_origin_id: self.target_origin_id,
780 cache_policy_id: self.cache_policy_id,
781 viewer_protocol_policy: self.viewer_protocol_policy,
782 allowed_methods: self.allowed_methods,
783 cached_methods: self.cached_methods,
784 compress: self.compress,
785 }
786 }
787}
788
789type_state!(
790 DistributionState,
791 DistributionStartState,
792 DistributionOriginState,
793);
794
795#[derive(Debug, Clone)]
796pub enum SigningBehavior {
797 Never,
798 NoOverride,
799 Always,
800}
801
802impl From<SigningBehavior> for String {
803 fn from(value: SigningBehavior) -> Self {
804 match value {
805 SigningBehavior::Never => "never".to_string(),
806 SigningBehavior::NoOverride => "no-override".to_string(),
807 SigningBehavior::Always => "always".to_string(),
808 }
809 }
810}
811
812#[derive(Debug, Clone)]
813pub enum SigningProtocol {
814 SigV4,
815}
816
817impl From<SigningProtocol> for String {
818 fn from(value: SigningProtocol) -> Self {
819 match value {
820 SigningProtocol::SigV4 => "sigv4".to_string(),
821 }
822 }
823}
824
825#[derive(Debug, Clone)]
826pub enum OriginAccessControlType {
827 S3,
828 MediaStore,
829 Lambda,
830 MediaPackageV2,
831}
832
833impl From<OriginAccessControlType> for String {
834 fn from(value: OriginAccessControlType) -> Self {
835 match value {
836 OriginAccessControlType::S3 => "s3".to_string(),
837 OriginAccessControlType::MediaStore => "mediastore".to_string(),
838 OriginAccessControlType::Lambda => "lambda".to_string(),
839 OriginAccessControlType::MediaPackageV2 => "mediapackagev2".to_string(),
840 }
841 }
842}
843
844pub struct OriginAccessControlBuilder {
848 id: Id,
849 name: String,
850 origin_access_control_type: OriginAccessControlType,
851 signing_behavior: SigningBehavior,
852 signing_protocol: SigningProtocol,
853}
854
855impl OriginAccessControlBuilder {
856 pub fn new(
865 id: &str,
866 name: &str,
867 origin_access_control_type: OriginAccessControlType,
868 signing_behavior: SigningBehavior,
869 signing_protocol: SigningProtocol,
870 ) -> Self {
871 Self {
872 id: Id(id.to_string()),
873 name: name.to_string(),
874 origin_access_control_type,
875 signing_behavior,
876 signing_protocol,
877 }
878 }
879
880 pub fn build(self, stack_builder: &mut StackBuilder) -> OriginAccessControlRef {
881 let resource_id = Resource::generate_id("OAC");
882 stack_builder.add_resource(OriginAccessControl {
883 id: self.id,
884 resource_id: resource_id.clone(),
885 r#type: OriginAccessControlDtoType::OriginAccessControlType,
886 properties: OriginControlProperties {
887 config: OriginAccessControlConfig {
888 name: self.name,
889 origin_access_control_type: self.origin_access_control_type.into(),
890 signing_behavior: self.signing_behavior.into(),
891 signing_protocol: self.signing_protocol.into(),
892 },
893 },
894 });
895 OriginAccessControlRef::internal_new(resource_id)
896 }
897}
898
899pub struct DistributionBuilder<T: DistributionState> {
926 phantom_data: PhantomData<T>,
927 id: Id,
928 enabled: bool,
929 default_cache_behavior: DefaultCacheBehavior,
930 price_class: Option<String>,
931 http_version: Option<String>,
932 aliases: Option<Vec<String>>,
933 cnames: Option<Vec<String>>,
934 ipv6_enabled: Option<bool>,
935 viewer_certificate: Option<ViewerCertificate>,
936 cache_behaviors: Option<Vec<CacheBehavior>>,
937 default_root_object: Option<String>,
938 origins: Option<Vec<Origin>>,
941}
942
943impl DistributionBuilder<DistributionStartState> {
944 pub fn new(id: &str, default_cache_behavior: DefaultCacheBehavior) -> Self {
950 Self {
951 phantom_data: Default::default(),
952 id: Id(id.to_string()),
953 enabled: true,
954 default_cache_behavior,
955 aliases: None,
956 cache_behaviors: None,
957 cnames: None,
958 default_root_object: None,
959 http_version: None,
960 ipv6_enabled: None,
961 origins: None,
962 price_class: None,
963 viewer_certificate: None,
964 }
965 }
966
967 pub fn origins(self, origins: Vec<Origin>) -> DistributionBuilder<DistributionOriginState> {
968 DistributionBuilder {
969 phantom_data: Default::default(),
970 origins: Some(origins),
971 id: self.id,
972 enabled: self.enabled,
973 default_cache_behavior: self.default_cache_behavior,
974 price_class: self.price_class,
975 http_version: self.http_version,
976 aliases: self.aliases,
977 cnames: self.cnames,
978 ipv6_enabled: self.ipv6_enabled,
979 viewer_certificate: self.viewer_certificate,
980 cache_behaviors: self.cache_behaviors,
981 default_root_object: self.default_root_object,
982 }
983 }
984}
985
986impl DistributionBuilder<DistributionOriginState> {
987 pub fn build(mut self, stack_builder: &mut StackBuilder) -> DistributionRef {
988 let mut origins = self.origins.take().expect("origins to be present in distribution origin state");
989 let resource_id = Resource::generate_id("CloudFrontDistribution");
990
991 origins
992 .iter_mut()
993 .filter(|o| o.s3_bucket_policy.is_some())
994 .map(|s3| {
995 let mut policy = s3
996 .s3_bucket_policy
997 .take()
998 .expect("just checked that this was present, only need to use it this one time");
999 let distro_id = get_att(&resource_id, "Id");
1000 let source_arn_value = join(
1001 "",
1002 vec![
1003 Value::String("arn:aws:cloudfront::".to_string()),
1004 get_ref(AWS_ACCOUNT_PSEUDO_PARAM),
1005 Value::String(":distribution/".to_string()),
1006 distro_id,
1007 ],
1008 );
1009 let distro_condition = json!({
1010 "StringEquals": {
1011 "AWS:SourceArn": source_arn_value
1012 }
1013 });
1014 policy
1015 .properties
1016 .policy_document
1017 .statements
1018 .iter_mut()
1019 .for_each(|v| v.condition = Some(distro_condition.clone()));
1020 policy
1021 })
1022 .for_each(|p| {
1023 stack_builder.add_resource(p);
1024 });
1025
1026 self.origins = Some(origins);
1027
1028 self.build_internal(resource_id, stack_builder)
1029 }
1030}
1031
1032impl<T: DistributionState> DistributionBuilder<T> {
1033 pub fn add_cache_behavior(mut self, behavior: CacheBehavior) -> Self {
1034 if let Some(mut behaviors) = self.cache_behaviors {
1035 behaviors.push(behavior);
1036 self.cache_behaviors = Some(behaviors);
1037 } else {
1038 self.cache_behaviors = Some(vec![behavior])
1039 }
1040 self
1041 }
1042
1043 pub fn aliases(self, aliases: Vec<String>) -> Self {
1044 Self {
1045 aliases: Some(aliases),
1046 ..self
1047 }
1048 }
1049
1050 pub fn cnames(self, cnames: Vec<String>) -> Self {
1052 Self {
1053 cnames: Some(cnames),
1054 ..self
1055 }
1056 }
1057
1058 pub fn price_class(self, price_class: PriceClass) -> Self {
1059 Self {
1060 price_class: Some(price_class.into()),
1061 ..self
1062 }
1063 }
1064
1065 pub fn http_version(self, http_version: HttpVersion) -> Self {
1066 Self {
1067 http_version: Some(http_version.into()),
1068 ..self
1069 }
1070 }
1071
1072 pub fn ipv6_enabled(self, enabled: bool) -> Self {
1073 Self {
1074 ipv6_enabled: Some(enabled),
1075 ..self
1076 }
1077 }
1078
1079 pub fn viewer_certificate(self, viewer_certificate: ViewerCertificate) -> Self {
1080 Self {
1081 viewer_certificate: Some(viewer_certificate),
1082 ..self
1083 }
1084 }
1085
1086 pub fn enabled(self, enabled: bool) -> Self {
1087 Self { enabled, ..self }
1088 }
1089
1090 pub fn default_root_object(self, default: DefaultRootObject) -> Self {
1091 Self {
1092 default_root_object: Some(default.0),
1093 ..self
1094 }
1095 }
1096
1097 fn build_internal(self, resource_id: String, stack_builder: &mut StackBuilder) -> DistributionRef {
1098 let config = DistributionConfig {
1099 enabled: self.enabled,
1100 default_cache_behavior: self.default_cache_behavior,
1101 aliases: self.aliases,
1102 cache_behaviors: self.cache_behaviors,
1103 cnames: self.cnames,
1104 default_root_object: self.default_root_object.unwrap_or_default(),
1105 http_version: self.http_version,
1106 ipv6_enabled: self.ipv6_enabled,
1107 price_class: self.price_class,
1108 viewer_certificate: self.viewer_certificate,
1109 origins: self.origins,
1110 origin_groups: None,
1111 };
1112 stack_builder.add_resource(Distribution {
1113 id: self.id,
1114 resource_id: resource_id.clone(),
1115 r#type: DistributionType::DistributionType,
1116 properties: DistributionProperties { config },
1117 });
1118
1119 DistributionRef::internal_new(resource_id)
1120 }
1121}