title: Programmatically setting up a static website using Amazon S3 and Route 53 APIs
body: <p>Assume I already have purchased a domain example.com with IP address 203.0.113.2. Using C# and the The Amazon Web Services SDK for .NET 2.0.2.2, I'd like to create a static website using a custom domain using Amazon S3 and Route 53. The manual process is described in the <a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html" rel="nofollow">Amazon documentation</a>.   </p>
  
  <p>When trying to create an alias, I get an exception with the message:</p>
  
  <pre><code>Invalid XML ; cvc-complex-type.2.4.a: Invalid content was found starting with element 'AliasTarget'.
  One of '{"https://route53.amazonaws.com/doc/2012-12-12/":ResourceRecords}' is expected.
  </code></pre>
  
  <p>First, I created or updated a bucket (e.g. "example.com") in Amazon S3. If it already existed, content is deleted.</p>
  
  <pre><code>using (var client = AWSClientFactory.CreateAmazonS3Client(RegionEndpoint.USWest1))
  {
      if (!S3BucketExists(name, client))
      {
          client.PutBucket(new PutBucketRequest
          {
              BucketName = name,
              BucketRegion = S3Region.USW1,
              CannedACL = S3CannedACL.PublicRead
          });
      }
      else
      {
          var request = new ListObjectsRequest
          {
              BucketName = name
          };
          var objects = client.ListObjects(request).S3Objects;
          foreach (var o in objects)
          {
              client.DeleteObject(new DeleteObjectRequest
              {
                  BucketName = name,
                  Key = o.Key
              });
          }
          client.PutACL(new PutACLRequest
          {
              CannedACL = S3CannedACL.PublicRead, 
              BucketName = name
          });
      }
  
      client.PutBucketWebsite(new PutBucketWebsiteRequest
      {
          BucketName = name,
          WebsiteConfiguration = new WebsiteConfiguration
          {
              ErrorDocument = "404.html",
              IndexDocumentSuffix = "index.html"
          }
      });
  
      CreateObject(name, client, "index.html", "text/html", "&lt;p&gt;The site is under maintenance&lt;/p&gt;");
      CreateObject(name, client, "404.html", "text/html", "&lt;p&gt;Not Found&lt;/p&gt;");
  }
  </code></pre>
  
  <p>S3BucketExists returns whether a bucket exist or not, and CreateObject creates a simple page and uploads it to the bucket.  Its omitted for brevity sake. I'm able to connect to the S3 hosted site without any problems.</p>
  
  <p>Then I use the Route 53 API to update an existing hosted zone or create one for "example.com". All resources, except for the SOA and NS entries are deleted.  </p>
  
  <pre><code>using (var client = AWSClientFactory.CreateAmazonRoute53Client())
  {
      var hostedZone = FindHostedZoneByName(client, domainName);
      if (hostedZone != null)
      {
          var resourceRecordSets = client.ListResourceRecordSets(new ListResourceRecordSetsRequest
          {
              HostedZoneId = hostedZone.Id,
          });
  
          bool hasElements = false;
          var request1 = new ChangeResourceRecordSetsRequest
          {
              HostedZoneId = hostedZone.Id,
              ChangeBatch = new ChangeBatch
              {
                  Changes = new List&lt;Change&gt;()
              }
          };
          foreach (var resourceRecordSet in resourceRecordSets.ResourceRecordSets)
          {
              switch (resourceRecordSet.Type)
              {
                  case "SOA":
                  case "NS":
                      continue;
              }
  
              var change = new Change
              {
                  Action = "DELETE",
                  ResourceRecordSet = resourceRecordSet
              };
              request1.ChangeBatch.Changes.Add(change);
              hasElements = true;
          }
  
          if (hasElements)
          {
              var response = client.ChangeResourceRecordSets(request1);
          }
      }
      else
      {
          hostedZone = CreateHostedZone(client, domainName);
      }
  
      var hostedZoneId = hostedZone.Id;
      var request = new ChangeResourceRecordSetsRequest
      {
          HostedZoneId = hostedZoneId,
          ChangeBatch = new ChangeBatch
          {
              Changes = new List&lt;Change&gt;
              {
                  new Change
                  {
                      Action = ChangeAction.CREATE,
                      ResourceRecordSet = new ResourceRecordSet
                      {
                          Name = GetQualifiedName(domainName),
                          Type = RRType.A,
                          TTL = 300,
                          AliasTarget = new AliasTarget()
                          {
                              HostedZoneId = "Z2F56UZL2M1ACD",
                              DNSName = "s3-website-us-west-1.amazonaws.com.",
                          },
                      },
                  },
              }
          }
      };
      client.ChangeResourceRecordSets(request);
  }
  </code></pre>
  
  <p>The hosted zone id ("Z2F56UZL2M1ACD") and DNS names ("s3-website-us-west-1.amazonaws.com.") are public knowledge and <a href="http://docs.aws.amazon.com/general/latest/gr/rande.html" rel="nofollow">documented on Amazon's website</a>. </p>
  
  <p>The call to ChangeResourceRecordSets throws the exception. I created an empty ResourceRecords list, with a A record of "203.0.113.2", but have not had any luck creating an alias.</p>
  
  <p>That said, I can manually create the alias to the Amazon S3 site afterwards using the "Route 53 Management Console".  I'm sure it's something small I'm missing.</p>