Ensure a Route53 Alias exists and is pointing at the given CloudFront
    distribution. An ``A`` record is always created, and if IPV6 is enabled on
    the given distribution, an ``AAAA`` record will be created as well. Also be
    aware that Alias records for CloudFront distributions are only permitted in
    non-private zones.

    name
        The name of the state definition.

    Distribution
        The name of the CloudFront distribution. Defaults to the value of
        ``name`` if not provided.

    HostedZoneId
        Id of the Route53 hosted zone within which the records should be created.

    DomainName
        The domain name associated with the Hosted Zone. Exclusive with HostedZoneId.

    ResourceRecordSet
        A Route53 Record Set (with AliasTarget section, suitable for use as an
        ``Alias`` record, if non-default settings are needed on the Alias)
        which should be pointed at the provided CloudFront distribution. Note
        that this MUST correlate with the Aliases set within the
        DistributionConfig section of the distribution.

        Some notes *specifically* about the ``AliasTarget`` subsection of the
        ResourceRecordSet:

        - If not specified, the ``DNSName`` sub-field will be populated by
          dereferencing ``Distribution`` above to the value of its
          ``DomainName`` attribute.

        - The HostedZoneId sub-field should not be provided -- it will be
          automatically populated with a ``magic`` AWS value.

        - The EvaluateTargetHealth can only be False on a CloudFront Alias.

        - The above items taken all together imply that, for most use-cases,
          the AliasTarget sub-section can be entirely omitted, as seen in the
          first code sample below.

        Lastly, note that if you set ``name`` to the desired ResourceRecordSet
        Name, you can entirely omit this parameter, as shown in the second
        example below.

    .. code-block:: yaml

        Add a Route53 Alias for my_distribution:
          boto_cloudfront.present:
          - Distribution: my_distribution
          - DomainName: saltstack.org.
          - ResourceRecordSet:
              Name: the-alias.saltstack.org.
        # This is even simpler - it uses the value of `name` for ResourceRecordSet.Name
        another-alias.saltstack.org.:
          boto_cloudfront.present:
          - Distribution: my_distribution
          - DomainName: saltstack.org.

def route53_alias_present(name, region=None, key=None, keyid=None, profile=None, **kwargs):
    '''
    Ensure a Route53 Alias exists and is pointing at the given CloudFront
    distribution. An ``A`` record is always created, and if IPV6 is enabled on
    the given distribution, an ``AAAA`` record will be created as well. Also be
    aware that Alias records for CloudFront distributions are only permitted in
    non-private zones.

    name
        The name of the state definition.

    Distribution
        The name of the CloudFront distribution. Defaults to the value of
        ``name`` if not provided.

    HostedZoneId
        Id of the Route53 hosted zone within which the records should be created.

    DomainName
        The domain name associated with the Hosted Zone. Exclusive with HostedZoneId.

    ResourceRecordSet
        A Route53 Record Set (with AliasTarget section, suitable for use as an
        ``Alias`` record, if non-default settings are needed on the Alias)
        which should be pointed at the provided CloudFront distribution. Note
        that this MUST correlate with the Aliases set within the
        DistributionConfig section of the distribution.

        Some notes *specifically* about the ``AliasTarget`` subsection of the
        ResourceRecordSet:

        - If not specified, the ``DNSName`` sub-field will be populated by
          dereferencing ``Distribution`` above to the value of its
          ``DomainName`` attribute.

        - The HostedZoneId sub-field should not be provided -- it will be
          automatically populated with a ``magic`` AWS value.

        - The EvaluateTargetHealth can only be False on a CloudFront Alias.

        - The above items taken all together imply that, for most use-cases,
          the AliasTarget sub-section can be entirely omitted, as seen in the
          first code sample below.

        Lastly, note that if you set ``name`` to the desired ResourceRecordSet
        Name, you can entirely omit this parameter, as shown in the second
        example below.

    .. code-block:: yaml

        Add a Route53 Alias for my_distribution:
          boto_cloudfront.present:
          - Distribution: my_distribution
          - DomainName: saltstack.org.
          - ResourceRecordSet:
              Name: the-alias.saltstack.org.
        # This is even simpler - it uses the value of `name` for ResourceRecordSet.Name
        another-alias.saltstack.org.:
          boto_cloudfront.present:
          - Distribution: my_distribution
          - DomainName: saltstack.org.

    '''
    MAGIC_CLOUDFRONT_HOSTED_ZONEID = 'Z2FDTNDATAQYW2'
    ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
    Distribution = kwargs['Distribution'] if 'Distribution' in kwargs else name
    ResourceRecordSet = kwargs.get('ResourceRecordSet', {})
    Name = ResourceRecordSet.get('Name', name)
    ResourceRecordSet['Name'] = Name

    res = __salt__['boto_cloudfront.get_distributions_by_comment'](Comment=Distribution,
            region=region, key=key, keyid=keyid, profile=profile)
    if res is None:
        msg = 'Error resolving CloudFront distribution `{}` to a Resource ID.'.format(Distribution)
        log.error(msg)
        ret['comment'] = msg
        ret['result'] = False
        return ret
    if len(res) > 1:
        msg = 'Multiple CloudFront distibutions matched `{}`.'.format(Distribution)
        log.error(msg)
        ret['comment'] = msg
        ret['result'] = False
        return ret
    if not res:
        msg = 'No CloudFront distibutions matching `{}` found.'.format(Distribution)
        log.error(msg)
        ret['comment'] = msg
        ret['result'] = False
        return ret
    dist = res[0]

    Types = ('A', 'AAAA') if dist.get('IsIPV6Enabled', False) else ('A',)
    DNSName = dist.get('DomainName', '')
    Aliases = dist.get('Aliases', {}).get('Items', [])
    # AWS annoyance #437:
    #   Route53 "FQDNs" (correctly!) REQUIRE trailing periods...
    #   while CloudFront "FQDNs" don't PERMIT trailing periods...
    Aliases += [(a if a.endswith('.') else '{}.'.format(a)) for a in Aliases]
    if Name not in Aliases:
        msg = ('Route53 alias `{}` requested which is not mirrored in the `Aliases`'
              ' sub-section of the DistributionConfig.'.format(Name))
        log.error(msg)
        ret['comment'] = msg
        ret['result'] = False
        return ret

    changes = {'old': [], 'new': []}
    comments = []
    # Now mock out a route53 state def, and use the route53 rr_exists state to enforce it...
    AliasTarget = ResourceRecordSet.get('AliasTarget', {})
    AliasTarget['DNSName'] = AliasTarget['DNSName'] if 'DNSName' in AliasTarget else DNSName
    AliasTarget['DNSName'] += '' if AliasTarget['DNSName'].endswith('.') else '.'  # GRRRR!
    AliasTarget['HostedZoneId'] = MAGIC_CLOUDFRONT_HOSTED_ZONEID
    AliasTarget['EvaluateTargetHealth'] = False   # Route53 limitation
    ResourceRecordSet['name'] = Name
    ResourceRecordSet['AliasTarget'] = AliasTarget
    ResourceRecordSet['PrivateZone'] = False      # Route53 limitation
    ResourceRecordSet['DomainName'] = kwargs.get('DomainName')
    ResourceRecordSet['HostedZoneId'] = kwargs.get('HostedZoneId')
    ResourceRecordSet.update({'region': region, 'key': key, 'keyid': keyid, 'profile': profile})
    for Type in Types:
        ResourceRecordSet['Type'] = Type
        # Checking for `test=True` will occur in the called state....
        log.debug('Calling state function `boto3_route53.rr_present()` with args:  `%s`',
                ResourceRecordSet)
        res = __states__['boto3_route53.rr_present'](**ResourceRecordSet)
        ret['result'] = res['result']
        comments += [res['comment']]
        if ret['result'] not in (True, None):
            break
        changes['old'] += [res['changes']['old']] if res['changes'].get('old') else []
        changes['new'] += [res['changes']['new']] if res['changes'].get('new') else []
    ret['changes'].update({'old': changes['old']}) if changes.get('old') else None
    ret['changes'].update({'new': changes['new']}) if changes.get('new') else None
    ret['comment'] = '  '.join(comments)
    return ret