How To: Use Go Daddy domain in AWS
Step-by-step guide to migrating your GoDaddy domain to AWS using S3, CloudFormation, ACM & CloudFront. This article will walk you through what you need to do step by step.
Pre-requisites
- Owning GoDaddy domain
- AWS Account
Create certificate in in AWS
This is the most important step in this tutorial. We are going to create an ACM certificate for a domain we own in GoDaddy. So let’s create one using the CloudFormation template:
AWSTemplateFormatVersion: 2010-09-09
Description: Cloudfront distribution for GoDaddy domain
Parameters:
GoDaddyDomainName:
Type: String
Resources:
DistributionCertificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref GoDaddyDomainName
ValidationMethod: DNS
SubjectAlternativeNames:
- !Sub www.${GoDaddyDomainName}
It’s important to create this certificate in us-east-1 region to use it with CloudFront
After running this CloudFormation using:
aws cloudformation deploy \
--template-file ./cloudfront.yaml \
--stack-name <YOUR_CLOUDFRONT_STACK_NAME> \
--region us-east-1 \
--parameter-overrides \
GoDaddyDomainName="<YOUR_GO_DADDY_DOMAIN>"
After running this command, your CloudFormation stack will be stuck on update with a message like the one on screen below:
What does it mean? Let’s go to the ACM and check the status of our certificate. What you will see in the ACM view is that the certificate validation is in Pending validation
status. This means that you need to confirm that you are the owner of this domain. You will need to prove this to AWS by adding some DNS records. Let’s do this now.
Certificate validation
The whole purpose of this process is to confirm that you are the real owner of the domain. To do this, we need to add CNAME records for the DomainName
defined in CloudFormation, as well as for any SubjectAlternativeNames
with values generated in ACM. So let’s do it:
- Go to ACM in AWS Console
- Find and open your certificate. Here you will find all the names for your certificate along with all the certificate details. On the screen below you can see what it looks like for a domain that has already been validated and one that is still in the
Pending Validation
state.
3 — Go to yours GoDaddy domain portfolio and then open DNS tab
4 — Add DNS CNAME Records.
You will need to add new CNAME records in GoDaddy for DomainName
and for any SubjectAlternativeNames
used by ACM to validate ownership. We need to pass the values found in the AWS Certificate Manager into the GoDaddy DNS view. The whole process will look like the graphic below:
In case graphic is not clear enough. Here let’s go into detailed explanation. Values that we are going to use from ACM are:
Domain: how-to.cloud
Name: _b3782c7cb6c4eebb755c20bc63d843a0.how-to.cloud.
Value: _692a3701368acf11ee44c403b23947c3.nbnhgqgzdr.acm-validations.aws.
and
Subdomain: www.how-to.cloud
Name: _c729485664bf15f9b455db9d07a7eae0.www.how-to.cloud.
Value: _10188566a7406ede1d4330648bade500.nbnhgqgzdr.acm-validations.aws.
Based on that we have to create 2 CNAME records in GoDaddy:
Type: CNAME
Name: _b3782c7cb6c4eebb755c20bc63d843a0
Value: _692a3701368acf11ee44c403b23947c3.nbnhgqgzdr.acm-validations.aws.
and
Type: CNAME
Name: _c729485664bf15f9b455db9d07a7eae0.www
Value: _10188566a7406ede1d4330648bade500.nbnhgqgzdr.acm-validations.aws.
Please keep in mind that in case of creating GoDaddy record for Name
of DNS record you have to remove your domain name from the end of value found in ACM. In our case: how-to.cloud
. It’s really important. Also remember that subdomain should be left there.
For example:
Before: _c729485664bf15f9b455db9d07a7eae0.www.how-to.cloud.
Afer: _c729485664bf15f9b455db9d07a7eae0.www
Here is our final result:
Congratulations! You’ve done it. In a few moments it will be confirmed by ACM and your CloudFormation deployment will be complete. Unfortunately, this step had to be done manually, but it’s a one-time thing. Now that we have a proper certificate, let’s create the CloudFront that will serve your whatever. In our case it will be a simple S3 bucket with a simple website.
Source setup
In the case of this example, we will be using an S3-based website hosted in a single region. So let’s start by creating a simple S3 bucket. So let’s create one. We will be working in us-east-1 for simplicity’s sake, to be able to use imports later. However, remember that you can create S3 buckets in any region, or even from multiple regions using cross-region replication, and serve the data using OriginGroups.
AWSTemplateFormatVersion: 2010-09-09
Parameters:
BucketName:
Type: String
Resources:
WebsiteBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
VersioningConfiguration:
Status: Enabled
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- Status: Enabled
ExpiredObjectDeleteMarker: true
NoncurrentVersionExpiration:
NewerNoncurrentVersions: 3
NoncurrentDays: 30
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 1
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
WebsiteBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WebsiteBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: InternalAccountAccess
Effect: Allow
Principal:
AWS: !Ref AWS::AccountId
Action:
- s3:*
Resource:
- !Sub arn:aws:s3:::${BucketName}
- !Sub arn:aws:s3:::${BucketName}/*
Outputs:
RegionalBucketURL:
Value: !GetAtt WebsiteBucket.RegionalDomainName
Description: Regional Bucket URL
Export:
Name: GO-DADDY-BUCKET::RegionalURL
You can deploy it with:
aws cloudformation deploy \
--template-file ./s3.yaml \
--stack-name <YOUR_STACK_NAME> \
--region us-east-1 \
--parameter-overrides \
BucketName=<YOUR_BUCKET_NAME> \
--capabilities CAPABILITY_IAM
CloudFront deployment
Now let’s deploy CloudFront that will serve content from our S3 Bucket.
This CloudFront setup is not production ready and I advise to adjust it especiallyt that it has caching disabled
AWSTemplateFormatVersion: 2010-09-09
Description: Cloudfront distribution for GoDaddy domain
Parameters:
GoDaddyDomainName:
Type: String
Resources:
DistributionCertificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref GoDaddyDomainName
ValidationMethod: DNS
SubjectAlternativeNames:
- !Sub www.${GoDaddyDomainName}
Distribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Aliases:
- !Ref GoDaddyDomainName
- !Sub www.${GoDaddyDomainName}
Enabled: true
DefaultRootObject: index.html
DefaultCacheBehavior:
MinTTL: 0
DefaultTTL: 0
MaxTTL: 0
AllowedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
# CachingDisabled
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-response-headers-policies.html
# CORS-With-Preflight
ResponseHeadersPolicyId: 5cc3b908-e619-4b99-88e5-2cf7f45965bd
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html
# CORS-S3Origin
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf
TargetOriginId: default-website-bucket
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_100
Origins:
- Id: default-website-bucket
DomainName:
Fn::ImportValue: GO-DADDY-BUCKET::RegionalURL
OriginPath: ''
OriginAccessControlId: !Ref PrivateS3OriginAccessControlConfig
S3OriginConfig:
OriginAccessIdentity: ''
ViewerCertificate:
AcmCertificateArn: !Ref DistributionCertificate
SslSupportMethod: sni-only
MinimumProtocolVersion: TLSv1.2_2019
PrivateS3OriginAccessControlConfig:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: !Sub Access control for ${GoDaddyDomainName} distribution
Name: !Sub ${GoDaddyDomainName}-${AWS::Region}
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
Outputs:
CloudFrontDistributionId:
Value: !Ref Distribution
Description: CloudFront Distribution Id
Export:
Name: GO-DADDY-CLOUDFRONT::DistributionId
At this stage, distribution won’t work because CloudFront doesn’t have access to S3, but let’s see how it looks. Let’s upload some index.html to our S3 bucket. I will use something as fancy as:
<!doctype html>
<html>
<body>
Hello from GoDaddy domain
</body>
</html>
What we can see in the response is:
➜ ~ http https://d1syklr40iud1b.cloudfront.net
HTTP/1.1 403 Forbidden
Connection: keep-alive
Content-Type: application/xml
Date: Tue, 06 Jun 2023 20:00:17 GMT
Server: AmazonS3
Transfer-Encoding: chunked
Vary: Origin
Via: 1.1 231be1c97cc722fa08b64d21072ebfac.cloudfront.net (CloudFront)
X-Amz-Cf-Id: yv--rxxVk4Koq0fIQ2MYBPjkZWRMvibvkMJltgWM3oyPFGPNO3NI5A==
X-Amz-Cf-Pop: FRA60-P3
X-Cache: Error from cloudfront
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>758RDK9Q1EMA2KXH</RequestId>
<HostId>uIhm855Br67zUN9ZstNoz3H0Gu7P7zZa1VGzSHqVHACppvyjlbdB/I5s4nQBHqbBRD5dmrkF6kg=</HostId>
</Error>
We can confirm in CloudTrail Lake that request was blocked when CloudFront called right object with simple query:
SELECT *
FROM
<YOUR LAKE ID>
WHERE
requestID = '758RDK9Q1EMA2KXH'
and we can find trail of that action where we can see that CloudFront had AccessDenied.
{
"eventVersion": "1.08",
"userIdentity": "{type=AWSService, principalid=null, arn=null, accountid=null, accesskeyid=null, username=null, sessioncontext=null, invokedby=cloudfront.amazonaws.com, identityprovider=null, credentialid=null, onbehalfof=null}",
"eventTime": "2023-06-06 20:00:17.000",
"eventSource": "s3.amazonaws.com",
"eventName": "GetObject",
"awsRegion": "us-east-1",
"sourceIPAddress": "cloudfront.amazonaws.com",
"userAgent": "cloudfront.amazonaws.com",
"errorCode": "AccessDenied",
"errorMessage": "Access Denied",
"requestParameters": "{bucketName=how-to-cloud-us-east-1, Host=how-to-cloud-us-east-1.s3.us-east-1.amazonaws.com, key=index.html}",
"responseElements": "",
"additionalEventData": "{SignatureVersion=SigV4, CipherSuite=..., bytesTransferredIn=0, AuthenticationMethod=AuthHeader, x-amz-id-2=..., bytesTransferredOut=243}",
"requestID": "758RDK9Q1EMA2KXH",
"eventID": "...",
"readOnly": "true",
"resources": "[{accountid=null, type=AWS::S3::Object, arn=arn:aws:s3:::how-to-cloud-us-east-1/index.html, arnprefix=null}, {accountid=190192394281, type=AWS::S3::Bucket, arn=arn:aws:s3:::how-to-cloud-us-east-1, arnprefix=null}]",
"eventType": "AwsApiCall",
"apiVersion": "",
"managementEvent": "false",
"recipientAccountId": "190192394281",
"sharedEventID": "ad3ae96a-9dfa-44ee-949f-37d742a51c36",
"annotation": "",
"vpcEndpointId": "",
"serviceEventDetails": "",
"addendum": "",
"edgeDeviceDetails": "",
"insightDetails": "",
"eventCategory": "Data",
"tlsDetails": "",
"sessionCredentialFromConsole": ""
}
Allow access to S3 Bucket
In order to do that we need to update our S3 bucket policy. We have to add policy that will allow CloudFront to get objects from our S3 bucket, but only from our CloudFront distribution. In order to do that we have to specify distribution id in our policy explicitly or import it from CloudFront stack. Unfortunately there is this dependency cycle between our resources. To do this, we need to update the WebsiteBucketPolicy
in our S3 template using one of 2 variants:
Variant: Specify explicitly
Go to CloudFront console in AWS and find your distribution id. Next use that value in specify it explicitly in our template.
- Sid: GrantCloudfrontAccess
Effect: Allow
Principal:
Service: cloudfront.amazonaws.com
Action: s3:GetObject
Resource: !Sub arn:aws:s3:::${BucketName}/*
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/<YOUR_CLOUDFRONT_DISTRIBUTION_ID>
Variant: Use Import
Update WebsiteBucketPolicy
by adding new bucket policy. Keep in mind that this cycle in using imports between stacks might be an issue during stacks removal.
- Sid: GrantCloudfrontAccess
Effect: Allow
Principal:
Service: cloudfront.amazonaws.com
Action: s3:GetObject
Resource: !Sub arn:aws:s3:::${BucketName}/*
Condition:
StringEquals:
AWS:SourceArn:
Fn::ImportValue: GO-DADDY-BUCKET::RegionalURL
After updating S3 bucket policy we can see that we are getting our website successfully:
➜ ~ http https://d1syklr40iud1b.cloudfront.net
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 80
Content-Type: text/html
Date: Tue, 06 Jun 2023 20:21:26 GMT
ETag: "9e79ae67431b730f3ba99e943104c979"
Last-Modified: Tue, 06 Jun 2023 19:59:30 GMT
Server: AmazonS3
Vary: Origin
Via: 1.1 1f7753fcca5feaf6f5b544926db150c0.cloudfront.net (CloudFront)
X-Amz-Cf-Id: v8ZcLIwJImTZemSwXqz2HcDp1IyeyV-vFrAvUXs9Cu8YnmkxtBupbg==
X-Amz-Cf-Pop: WAW50-C1
X-Cache: Miss from cloudfront
x-amz-server-side-encryption: AES256
x-amz-version-id: taa6eGX7mwUaw_GS0rwR.HCAnF5yewV9
<!doctype html>
<html>
<body>
Hello from GoDaddy domain
</body>
</html>
Setup GoDaddy DNS Record
Okay, it works, but we supposed to use our GoDaddy Domain. So what we can do now? Let’s add another CNAME in GoDaddy for our CloudFront. In order to do that go back to GoDaddy DNS domain configuration and add record for subdomain like www with your CloudFront URL.
After adding such record we can see that it works great!
➜ ~ http https://www.how-to.cloud
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 80
Content-Type: text/html
Date: Tue, 06 Jun 2023 20:24:40 GMT
ETag: "9e79ae67431b730f3ba99e943104c979"
Last-Modified: Tue, 06 Jun 2023 19:59:30 GMT
Server: AmazonS3
Vary: Origin
Via: 1.1 50004ba6b399efd7e9feb3e04887ccc0.cloudfront.net (CloudFront)
X-Amz-Cf-Id: ST6sWBEvvRT0MvUiV0c3ny_5lKuLAE6ikyhaZ6tM6cVOkPyDCzc48g==
X-Amz-Cf-Pop: WAW50-C1
X-Cache: Miss from cloudfront
x-amz-server-side-encryption: AES256
x-amz-version-id: taa6eGX7mwUaw_GS0rwR.HCAnF5yewV9
<!doctype html>
<html>
<body>
Hello from GoDaddy domain
</body>
</html>
GoDaddy redirect
What about using DomainName without subdomain? It won’t work. GoDaddy do not support ANAME and ALIAS records so we have to create redirect.
Go to Forwarding
tab:
and add forwarding to our CloudFront like on screen below:
Now we can see that redirect works fine as we have in response:
HTTP Status — 301 Moved Permanently
Response header — Location: https://www.how-to.cloud
➜ ~ http -v how-to.cloud/
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: how-to.cloud
User-Agent: HTTPie/3.2.1
HTTP/1.1 301 Moved Permanently
Connection: keep-alive
Content-Length: 59
Content-Type: text/html; charset=utf-8
Date: Tue, 06 Jun 2023 20:38:12 GMT
Location: https://www.how-to.cloud
Server: ip-100-74-3-237.eu-west-2.compute.internal
X-Request-Id: bbec18ac-eafc-4475-ba0b-5063e0c0b3d0
<a href="https://www.how-to.cloud">Moved Permanently</a>.
Summary
That’s it, you can now use your domain and expose your AWS resources through CloudFront. I hope this article was informative and it saved you some time. Cheers!
References: