Repository: AnalogJ/lexicon

Issue:
Route53 provider does not handle >100 hosted zones
Hello,

I ran into an interesting problem while setting up a system with certbot + lexicon. We have almost 200 hosted zones in AWS Route 53 and we were getting a "No domains found." error which I finally traced to the _authenticate method. I hacked together some changes to this method so it makes repeated calls to get all the hosted zones before searching for the one for which the hosted zone id is needed. Based on the Boto3 docs it looks like even though this method has a MaxItems parameter, the absolute max is 100. 

I don't think this is the best written change, so I am just including it here for someone to perhaps include it in a future update.

Thanks.

```
    def _authenticate(self):
        """Determine the hosted zone id for the domain."""
        try:
            is_truncated=True
            next_dns_name=''
            next_hz_id=''
            hosted_zones=[]
            while(is_truncated):
                if(next_dns_name==''):
                    hosted_zones_dict = self.r53_client.list_hosted_zones_by_name()
                else:
                    hosted_zones_dict = self.r53_client.list_hosted_zones_by_name(DNSName=next_dns_name,HostedZoneId=next_hz_id)
                hosted_zones += hosted_zones_dict[
                    'HostedZones'
                ]
                is_truncated = hosted_zones_dict[
                    'IsTruncated'
                ]
                if(is_truncated):
                    next_dns_name = hosted_zones_dict[
                        'NextDNSName'
                    ]
                    next_hz_id = hosted_zones_dict[
                        'NextHostedZoneId'
                    ]
            hosted_zone = next(
                hz for hz in hosted_zones
                if self.filter_zone(hz)
            )
            self.domain_id = hosted_zone['Id']
        except StopIteration:
            raise Exception('No domain found')
```


Discussion:
I hit this issue too, your code works thank you! Here's to hoping it gets pulled

Resolving patch:
diff --git a/lexicon/providers/route53.py b/lexicon/providers/route53.py
--- a/lexicon/providers/route53.py
+++ b/lexicon/providers/route53.py
@@ -130,9 +130,28 @@ def str2bool(input_string):
     def _authenticate(self):
         """Determine the hosted zone id for the domain."""
         try:
-            hosted_zones = self.r53_client.list_hosted_zones_by_name()["HostedZones"]
+            is_truncated = True
+            next_dns_name = None
+            next_hz_id = None
+            hosted_zones = []
+
+            while is_truncated:
+                if not next_dns_name:
+                    new_zones = self.r53_client.list_hosted_zones_by_name()
+                else:
+                    new_zones = self.r53_client.list_hosted_zones_by_name(
+                        DNSName=next_dns_name, HostedZoneId=next_hz_id
+                    )
+
+                hosted_zones.extend(new_zones.get("HostedZones"))
+                is_truncated = new_zones.get("IsTruncated")
+
+                if is_truncated:
+                    next_dns_name = new_zones.get("NextDNSName")
+                    next_hz_id = new_zones.get("NextHostedZoneId")
+
             hosted_zone = next(hz for hz in hosted_zones if self.filter_zone(hz))
-            self.domain_id = hosted_zone["Id"]
+            self.domain_id = hosted_zone.get("Id")
         except StopIteration:
             raise Exception("No domain found")
 


Test patch: