The cdk version of this scenario (dynamic website hosting) will involve reference to a domain certificate in acm and hosted zone in Route 53. All other resource lifecycle management is handled by the cdk.
Domain management including TLS/SSL certificates are assumed knowledge at this point. If you need guidance please review the manual steps in the scenario page which has further cdk related details in cdk-static-hosting.
This app is actually multiple stacks with properties passed in from outputs of one class object to another's constructor (dependency injection). This was necessary due to issues related to the dependencies between the stacks and order of operations in resource construction. Breaking apart stacks and passing props makes the dependency graph explicit and maximizes developer control. We will start with the App and work backward to the details of the main stack and then back into the details of the parent stack passed into the child stack's constructor.
The cdk app launcher imports the stacks and constructs them in its own application scope (the parent of the cdk construct tree). A certificate arn is set as an environment variable mainly because it is a pain to handle it otherwise. Other environment variables are set in the github actions workflow.
The lambda stack passes its own function url property to the main stack with the distribution, bucket and dns. The main stack uses the property of the parent lambda stack as an argument to its constructor (dependency injection). This argument is used to set the distribution's origin to the lambda function url.
The main stack receives the function url and cert as props which are used to configure the distribution. ssr is used as a subdomain for the apex domain and that is assigned to the distribution as an alias (cname). A bucket and lambda are both configured as origins and behaviors are set to direct all traffic to the lambda except for the specific assets route.
Attaching the bucket policy based on the distribution's arn for an existing bucket should work, but it does not in practice. Either create a new bucket and apply the policy or apply the policy manually to an existing bucket as a workaround.