Repository: StackExchange/dnscontrol
Pull request #675 (2020-03-01 18:01:48)

Add support for Alias in Azure
Implement `AZURE_ALIAS` custom record type for Azure DNS
based on `R53_ALIAS`

Added Integration Tests

Actions #634 

vatsalyagoel: Yep it was missing there. Resolved

Patch:
diff --git a/.travis.yml b/.travis.yml
index 2e72d9f6..ec3b1fec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: go
 
 go:
-  - 1.13.x
+  - 1.14.x
 
 install: pwd
 
diff --git a/Dockerfile b/Dockerfile
index 919fcee6..4ebf2156 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.13-alpine AS build-env
+FROM golang:1.14-alpine AS build-env
 WORKDIR /go/src/github.com/StackExchange/dnscontrol
 ADD . .
 RUN apk update && apk add git
diff --git a/build/azure-pipelines/go-env.yaml b/build/azure-pipelines/go-env.yaml
index fa486b01..2ec28759 100644
--- a/build/azure-pipelines/go-env.yaml
+++ b/build/azure-pipelines/go-env.yaml
@@ -3,4 +3,4 @@
 steps:
 - task: GoTool@0
   inputs:
-    version: '1.13.6'
+    version: '1.14'
diff --git a/build/generate/featureMatrix.go b/build/generate/featureMatrix.go
index a34b597a..355c67e6 100644
--- a/build/generate/featureMatrix.go
+++ b/build/generate/featureMatrix.go
@@ -39,6 +39,7 @@ func generateFeatureMatrix() error {
 			{"TLSA", "Provider can manage TLSA records"},
 			{"TXTMulti", "Provider can manage TXT records with multiple strings"},
 			{"R53_ALIAS", "Provider supports Route 53 limited ALIAS"},
+			{"AZURE_ALIAS", "Provider supports Azure DNS limited ALIAS"},
 
 			{"dual host", "This provider is recommended for use in 'dual hosting' scenarios. Usually this means the provider allows full control over the apex NS records"},
 			{"create-domains", "This means the provider can automatically create domains that do not currently exist on your account. The 'dnscontrol create-domains' command will initialize any missing domains"},
@@ -80,6 +81,7 @@ func generateFeatureMatrix() error {
 		setCap("NAPTR", providers.CanUseNAPTR)
 		setCap("PTR", providers.CanUsePTR)
 		setCap("R53_ALIAS", providers.CanUseRoute53Alias)
+		setCap("AZURE_ALIAS", providers.CanUseAzureAlias)
 		setCap("SRV", providers.CanUseSRV)
 		setCap("SSHFP", providers.CanUseSSHFP)
 		setCap("TLSA", providers.CanUseTLSA)
diff --git a/docs/_functions/domain/AZURE_ALIAS.md b/docs/_functions/domain/AZURE_ALIAS.md
new file mode 100644
index 00000000..0034a2c7
--- /dev/null
+++ b/docs/_functions/domain/AZURE_ALIAS.md
@@ -0,0 +1,54 @@
+---
+name: AZURE_ALIAS
+parameters:
+  - name
+  - type
+  - target
+  - modifiers ...
+---
+
+AZURE_ALIAS is a Azure specific virtual record type that points a record at either another record or an Azure entity.
+It is analogous to a CNAME, but is usually resolved at request-time and served as an A record.
+Unlike CNAMEs, ALIAS records can be used at the zone apex (`@`)
+
+Unlike the regular ALIAS directive, AZURE_ALIAS is only supported on AZURE.
+Attempting to use AZURE_ALIAS on another provider than Azure will result in an error.
+
+The name should be the relative label for the domain.
+
+The type can be any of the following: 
+* A
+* AAAA
+* CNAME
+
+Target should be the Azure Id representing the target. It starts `/subscription/`. The resource id can be found in https://resources.azure.com/.
+
+The Target can :
+
+* Point to a public IP resource from a DNS `A/AAAA` record set.
+You can create an A/AAAA record set and make it an alias record set to point to a public IP resource (standard or basic).
+The DNS record set changes automatically if the public IP address changes or is deleted. 
+Dangling DNS records that point to incorrect IP addresses are avoided.
+There is a current limit of 20 alias records sets per resource.
+* Point to a Traffic Manager profile from a DNS `A/AAAA/CNAME` record set.
+You can create an A/AAAA or CNAME record set and use alias records to point it to a Traffic Manager profile.
+It's especially useful when you need to route traffic at a zone apex, as traditional CNAME records aren't supported for a zone apex.
+For example, say your Traffic Manager profile is myprofile.trafficmanager.net and your business DNS zone is contoso.com.
+You can create an alias record set of type A/AAAA for contoso.com (the zone apex) and point to myprofile.trafficmanager.net.
+* Point to an Azure Content Delivery Network (CDN) endpoint.
+This is useful when you create static websites using Azure storage and Azure CDN.
+* Point to another DNS record set within the same zone.
+Alias records can reference other record sets of the same type.
+For example, a DNS CNAME record set can be an alias to another CNAME record set. 
+This arrangement is useful if you want some record sets to be aliases and some non-aliases.
+
+{% include startExample.html %}
+{% highlight js %}
+
+D("example.com", REGISTRAR, DnsProvider("AZURE_DNS"),
+  AZURE_ALIAS("foo", "A", "/subscriptions/726f8cd6-6459-4db4-8e6d-2cd2716904e2/resourceGroups/test/providers/Microsoft.Network/trafficManagerProfiles/testpp2"), // record for traffic manager
+  AZURE_ALIAS("foo", "CNAME", "/subscriptions/726f8cd6-6459-4db4-8e6d-2cd2716904e2/resourceGroups/test/providers/Microsoft.Network/dnszones/example.com/A/quux."), // record in the same zone
+);
+
+{%endhighlight%}
+{% include endExample.html %}
\ No newline at end of file
diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html
index 604cf175..5fde8ddf 100644
--- a/docs/_includes/matrix.html
+++ b/docs/_includes/matrix.html
@@ -263,7 +263,7 @@
 		<td class="danger">
 			<i class="fa fa-times text-danger" aria-hidden="true"></i>
 		</td>
-		<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="Only supported for Azure Resources. Not yet implemented">
+		<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="Azure DNS does not provide a generic ALIAS functionality. Use AZURE_ALIAS instead.">
 			<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
 		</td>
 		<td><i class="fa fa-minus dim"></i></td>
@@ -706,9 +706,7 @@
 	<tr>
 		<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports Route 53 limited ALIAS">R53_ALIAS</th>
 		<td><i class="fa fa-minus dim"></i></td>
-		<td class="danger">
-			<i class="fa fa-times text-danger" aria-hidden="true"></i>
-		</td>
+		<td><i class="fa fa-minus dim"></i></td>
 		<td><i class="fa fa-minus dim"></i></td>
 		<td><i class="fa fa-minus dim"></i></td>
 		<td><i class="fa fa-minus dim"></i></td>
@@ -736,6 +734,35 @@
 		<td><i class="fa fa-minus dim"></i></td>
 		<td><i class="fa fa-minus dim"></i></td>
 		</tr>
+	<tr>
+		<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports Azure DNS limited ALIAS">AZURE_ALIAS</th>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td class="success">
+			<i class="fa fa-check text-success" aria-hidden="true"></i>
+		</td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		<td><i class="fa fa-minus dim"></i></td>
+		</tr>
 	<tr>
 		<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="This provider is recommended for use in &#39;dual hosting&#39; scenarios. Usually this means the provider allows full control over the apex NS records">dual host</th>
 		<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="This driver does not manage NS records, so should not be used for dual-host scenarios">
diff --git a/docs/alias.md b/docs/alias.md
index 056c69df..95c19dd0 100644
--- a/docs/alias.md
+++ b/docs/alias.md
@@ -24,4 +24,4 @@ func init() {
 3. CNAMEs at `@` are disallowed, but ALIAS is allowed.
 4. Cloudflare does not have a native ALIAS type, but CNAMEs behave similarly. The Cloudflare provider "rewrites" ALIAS records to CNAME as it sees them. Other providers may not need this step.
 5. Route 53 requires the use of R53_ALIAS instead of ALIAS.
-
+6. Azure DNS requires the use of AZURE_ALIAS instead of ALIAS.
diff --git a/go.mod b/go.mod
index b9fdaedd..b2907f69 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/StackExchange/dnscontrol/v2
 
-go 1.13
+go 1.14
 
 require (
 	cloud.google.com/go v0.41.1-0.20190703172311-335e9e09b93e // indirect
diff --git a/models/domain.go b/models/domain.go
index b08bcf55..ad240733 100644
--- a/models/domain.go
+++ b/models/domain.go
@@ -79,7 +79,7 @@ func (dc *DomainConfig) Punycode() error {
 			if err != nil {
 				return err
 			}
-		case "A", "AAAA", "CAA", "NAPTR", "SOA", "SSHFP", "TXT", "TLSA":
+		case "A", "AAAA", "CAA", "NAPTR", "SOA", "SSHFP", "TXT", "TLSA", "AZURE_ALIAS":
 			// Nothing to do.
 		default:
 			msg := fmt.Sprintf("Punycode rtype %v unimplemented", rec.Type)
diff --git a/models/record.go b/models/record.go
index c838e9bb..b6affe1c 100644
--- a/models/record.go
+++ b/models/record.go
@@ -95,6 +95,7 @@ type RecordConfig struct {
 	TlsaMatchingType uint8             `json:"tlsamatchingtype,omitempty"`
 	TxtStrings       []string          `json:"txtstrings,omitempty"` // TxtStrings stores all strings (including the first). Target stores only the first one.
 	R53Alias         map[string]string `json:"r53_alias,omitempty"`
+	AzureAlias       map[string]string `json:"azure_alias,omitempty"`
 
 	Original interface{} `json:"-"` // Store pointer to provider-specific record object. Used in diffing.
 }
@@ -307,6 +308,12 @@ func (rc *RecordConfig) Key() RecordKey {
 			// label with different alias types are considered separate.
 			t = fmt.Sprintf("%s_%s", t, v)
 		}
+	} else if rc.AzureAlias != nil {
+		if v, ok := rc.AzureAlias["type"]; ok {
+			// Azure aliases append their alias type, so that records for the same
+			// label with different alias types are considered separate.
+			t = fmt.Sprintf("%s_%s", t, v)
+		}
 	}
 	return RecordKey{rc.NameFQDN, t}
 }
diff --git a/models/target.go b/models/target.go
index ad81c627..f443a596 100644
--- a/models/target.go
+++ b/models/target.go
@@ -52,6 +52,9 @@ func (rc *RecordConfig) GetTargetCombined() string {
 		case "R53_ALIAS":
 			// Differentiate between multiple R53_ALIASs on the same label.
 			return fmt.Sprintf("%s atype=%s zone_id=%s", rc.Target, rc.R53Alias["type"], rc.R53Alias["zone_id"])
+		case "AZURE_ALIAS":
+			// Differentiate between multiple AZURE_ALIASs on the same label.
+			return fmt.Sprintf("%s atype=%s", rc.Target, rc.AzureAlias["type"])
 		case "SOA":
 			return fmt.Sprintf("%s %v %d %d %d %d %d", rc.Target, rc.SoaMbox, rc.SoaSerial, rc.SoaRefresh, rc.SoaRetry, rc.SoaExpire, rc.SoaMinttl)
 		default:
@@ -101,6 +104,8 @@ func (rc *RecordConfig) GetTargetDebug() string {
 		content += fmt.Sprintf(" caatag=%s caaflag=%d", rc.CaaTag, rc.CaaFlag)
 	case "R53_ALIAS":
 		content += fmt.Sprintf(" type=%s zone_id=%s", rc.R53Alias["type"], rc.R53Alias["zone_id"])
+	case "AZURE_ALIAS":
+		content += fmt.Sprintf(" type=%s", rc.AzureAlias["type"])
 	default:
 		panic(fmt.Errorf("rc.String rtype %v unimplemented", rc.Type))
 		// We panic so that we quickly find any switch statements
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..870d78e9
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,11 @@
+{
+  "requires": true,
+  "lockfileVersion": 1,
+  "dependencies": {
+    "prettier": {
+      "version": "1.19.1",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
+      "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew=="
+    }
+  }
+}
diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js
index 1e292039..2050a8f3 100644
--- a/pkg/js/helpers.js
+++ b/pkg/js/helpers.js
@@ -165,6 +165,31 @@ var AAAA = recordBuilder('AAAA');
 // ALIAS(name,target, recordModifiers...)
 var ALIAS = recordBuilder('ALIAS');
 
+// AZURE_ALIAS(name, type, target, recordModifiers...)
+var AZURE_ALIAS = recordBuilder('AZURE_ALIAS', {
+    args: [
+        ['name', _.isString],
+        ['type', validateAzureAliasType],
+        ['target', _.isString],
+    ],
+    transform: function(record, args, modifier) {
+        record.name = args.name;
+        record.target = args.target;
+        if (_.isObject(record.azure_alias)) {
+            record.azure_alias['type'] = args.type;
+        } else {
+            record.azure_alias = { type: args.type };
+        }
+    },
+});
+
+function validateAzureAliasType(value) {
+    if (!_.isString(value)) {
+        return false;
+    }
+    return ['A', 'AAAA', 'CNAME'].indexOf(value) !== -1;
+}
+
 // R53_ALIAS(name, target, type, recordModifiers...)
 var R53_ALIAS = recordBuilder('R53_ALIAS', {
     args: [
@@ -210,7 +235,7 @@ function validateR53AliasType(value) {
             'SPF',
             'SRV',
             'NAPTR',
-        ].indexOf(value) != -1
+        ].indexOf(value) !== -1
     );
 }
 
@@ -741,7 +766,8 @@ function CAA_BUILDER(value) {
         (!value.issue && !value.issuewild) ||
         (value.issue &&
             value.issue.length == 0 &&
-            value.issuewild && value.issuewild.length == 0)
+            value.issuewild &&
+            value.issuewild.length == 0)
     ) {
         throw 'CAA_BUILDER requires at least one entry at issue or issuewild';
     }
diff --git a/pkg/js/static.go b/pkg/js/static.go
index a7adadd8..ce479502 100644
--- a/pkg/js/static.go
+++ b/pkg/js/static.go
@@ -212,103 +212,105 @@ var _escData = map[string]*_escFile{
 	"/helpers.js": {
 		name:    "helpers.js",
 		local:   "pkg/js/helpers.js",
-		size:    22107,
+		size:    22817,
 		modtime: 0,
 		compressed: `
-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
+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
+Ef83AAD//yJmQYUhWQAA
 `,
 	},
 }
diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go
index 8f0122c8..fe81d66a 100644
--- a/pkg/normalize/validate.go
+++ b/pkg/normalize/validate.go
@@ -465,6 +465,7 @@ func init() {
 		{"SSHFP", providers.CanUseSSHFP},
 		{"SRV", providers.CanUseSRV},
 		{"TLSA", providers.CanUseTLSA},
+		{"AZURE_ALIAS", providers.CanUseAzureAlias},
 	}
 }
 
diff --git a/providers/azuredns/azureDnsProvider.go b/providers/azuredns/azureDnsProvider.go
index 0e68c271..a8ea397f 100644
--- a/providers/azuredns/azureDnsProvider.go
+++ b/providers/azuredns/azureDnsProvider.go
@@ -17,10 +17,11 @@ import (
 )
 
 type azureDnsProvider struct {
-	zonesClient   *adns.ZonesClient
-	recordsClient *adns.RecordSetsClient
-	zones         map[string]*adns.Zone
-	resourceGroup *string
+	zonesClient    *adns.ZonesClient
+	recordsClient  *adns.RecordSetsClient
+	zones          map[string]*adns.Zone
+	resourceGroup  *string
+	subscriptionId *string
 }
 
 func newAzureDnsDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
@@ -41,7 +42,7 @@ func newAzureDns(m map[string]string, metadata json.RawMessage) (*azureDnsProvid
 
 	zonesClient.Authorizer = authorizer
 	recordsClient.Authorizer = authorizer
-	api := &azureDnsProvider{zonesClient: &zonesClient, recordsClient: &recordsClient, resourceGroup: to.StringPtr(rg)}
+	api := &azureDnsProvider{zonesClient: &zonesClient, recordsClient: &recordsClient, resourceGroup: to.StringPtr(rg), subscriptionId: to.StringPtr(subId)}
 	err := api.getZones()
 	if err != nil {
 		return nil, err
@@ -50,7 +51,7 @@ func newAzureDns(m map[string]string, metadata json.RawMessage) (*azureDnsProvid
 }
 
 var features = providers.DocumentationNotes{
-	providers.CanUseAlias:            providers.Cannot("Only supported for Azure Resources. Not yet implemented"),
+	providers.CanUseAlias:            providers.Cannot("Azure DNS does not provide a generic ALIAS functionality. Use AZURE_ALIAS instead."),
 	providers.DocCreateDomains:       providers.Can(),
 	providers.DocDualHost:            providers.Can("Azure does not permit modifying the existing NS records, only adding/removing additional records."),
 	providers.DocOfficiallySupported: providers.Can(),
@@ -58,15 +59,16 @@ var features = providers.DocumentationNotes{
 	providers.CanUseSRV:              providers.Can(),
 	providers.CanUseTXTMulti:         providers.Can(),
 	providers.CanUseCAA:              providers.Can(),
-	providers.CanUseRoute53Alias:     providers.Cannot(),
 	providers.CanUseNAPTR:            providers.Cannot(),
 	providers.CanUseSSHFP:            providers.Cannot(),
 	providers.CanUseTLSA:             providers.Cannot(),
 	providers.CanGetZones:            providers.Can(),
+	providers.CanUseAzureAlias:       providers.Can(),
 }
 
 func init() {
 	providers.RegisterDomainServiceProviderType("AZURE_DNS", newAzureDnsDsp, features)
+	providers.RegisterCustomRecordType("AZURE_ALIAS", "AZURE_DNS", "")
 }
 
 func (a *azureDnsProvider) getExistingZones() (*adns.ZoneListResult, error) {
@@ -229,7 +231,7 @@ func (a *azureDnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
 				return nil, fmt.Errorf("no record set found to delete. Name: '%s'. Type: '%s'", k.NameFQDN, k.Type)
 			}
 		} else {
-			rrset, recordType := recordToNative(k, recs)
+			rrset, recordType := a.recordToNative(k, recs)
 			var recordName string
 			for _, r := range recs {
 				i := int64(r.TTL)
@@ -284,13 +286,13 @@ func (a *azureDnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
 func nativeToRecordType(recordType *string) adns.RecordType {
 	recordTypeStripped := strings.TrimPrefix(*recordType, "Microsoft.Network/dnszones/")
 	switch recordTypeStripped {
-	case "A":
+	case "A", "AZURE_ALIAS_A":
 		return adns.A
-	case "AAAA":
+	case "AAAA", "AZURE_ALIAS_AAAA":
 		return adns.AAAA
 	case "CAA":
 		return adns.CAA
-	case "CNAME":
+	case "CNAME", "AZURE_ALIAS_CNAME":
 		return adns.CNAME
 	case "MX":
 		return adns.MX
@@ -321,6 +323,17 @@ func nativeToRecords(set *adns.RecordSet, origin string) []*models.RecordConfig
 				_ = rc.SetTarget(*rec.Ipv4Address)
 				results = append(results, rc)
 			}
+		} else {
+			rc := &models.RecordConfig{
+				Type: "AZURE_ALIAS",
+				TTL:  uint32(*set.TTL),
+				AzureAlias: map[string]string{
+					"type": "A",
+				},
+			}
+			rc.SetLabelFromFQDN(*set.Fqdn, origin)
+			_ = rc.SetTarget(*set.TargetResource.ID)
+			results = append(results, rc)
 		}
 	case "Microsoft.Network/dnszones/AAAA":
 		if set.AaaaRecords != nil {
@@ -331,13 +344,37 @@ func nativeToRecords(set *adns.RecordSet, origin string) []*models.RecordConfig
 				_ = rc.SetTarget(*rec.Ipv6Address)
 				results = append(results, rc)
 			}
+		} else {
+			rc := &models.RecordConfig{
+				Type: "AZURE_ALIAS",
+				TTL:  uint32(*set.TTL),
+				AzureAlias: map[string]string{
+					"type": "AAAA",
+				},
+			}
+			rc.SetLabelFromFQDN(*set.Fqdn, origin)
+			_ = rc.SetTarget(*set.TargetResource.ID)
+			results = append(results, rc)
 		}
 	case "Microsoft.Network/dnszones/CNAME":
-		rc := &models.RecordConfig{TTL: uint32(*set.TTL)}
-		rc.SetLabelFromFQDN(*set.Fqdn, origin)
-		rc.Type = "CNAME"
-		_ = rc.SetTarget(*set.CnameRecord.Cname)
-		results = append(results, rc)
+		if set.CnameRecord != nil {
+			rc := &models.RecordConfig{TTL: uint32(*set.TTL)}
+			rc.SetLabelFromFQDN(*set.Fqdn, origin)
+			rc.Type = "CNAME"
+			_ = rc.SetTarget(*set.CnameRecord.Cname)
+			results = append(results, rc)
+		} else {
+			rc := &models.RecordConfig{
+				Type: "AZURE_ALIAS",
+				TTL:  uint32(*set.TTL),
+				AzureAlias: map[string]string{
+					"type": "CNAME",
+				},
+			}
+			rc.SetLabelFromFQDN(*set.Fqdn, origin)
+			_ = rc.SetTarget(*set.TargetResource.ID)
+			results = append(results, rc)
+		}
 	case "Microsoft.Network/dnszones/NS":
 		for _, rec := range *set.NsRecords {
 			rc := &models.RecordConfig{TTL: uint32(*set.TTL)}
@@ -401,7 +438,7 @@ func nativeToRecords(set *adns.RecordSet, origin string) []*models.RecordConfig
 	return results
 }
 
-func recordToNative(recordKey models.RecordKey, recordConfig []*models.RecordConfig) (*adns.RecordSet, adns.RecordType) {
+func (a *azureDnsProvider) recordToNative(recordKey models.RecordKey, recordConfig []*models.RecordConfig) (*adns.RecordSet, adns.RecordType) {
 	recordSet := &adns.RecordSet{Type: to.StringPtr(recordKey.Type), RecordSetProperties: &adns.RecordSetProperties{}}
 	for _, rec := range recordConfig {
 		switch recordKey.Type {
@@ -450,11 +487,15 @@ func recordToNative(recordKey models.RecordKey, recordConfig []*models.RecordCon
 				recordSet.CaaRecords = &[]adns.CaaRecord{}
 			}
 			*recordSet.CaaRecords = append(*recordSet.CaaRecords, adns.CaaRecord{Value: to.StringPtr(rec.Target), Tag: to.StringPtr(rec.CaaTag), Flags: to.Int32Ptr(int32(rec.CaaFlag))})
+		case "AZURE_ALIAS_A", "AZURE_ALIAS_AAAA", "AZURE_ALIAS_CNAME":
+			*recordSet.Type = rec.AzureAlias["type"]
+			recordSet.TargetResource = &adns.SubResource{ID: to.StringPtr(rec.Target)}
 		default:
 			panic(fmt.Errorf("rc.String rtype %v unimplemented", recordKey.Type))
 		}
 	}
-	return recordSet, nativeToRecordType(to.StringPtr(recordKey.Type))
+
+	return recordSet, nativeToRecordType(to.StringPtr(*recordSet.Type))
 }
 
 func (a *azureDnsProvider) fetchRecordSets(zoneName string) ([]*adns.RecordSet, error) {
diff --git a/providers/capabilities.go b/providers/capabilities.go
index 1bb52f62..8f8a5d61 100644
--- a/providers/capabilities.go
+++ b/providers/capabilities.go
@@ -56,6 +56,9 @@ const (
 
 	// CanGetZoe indicates the provider supports the get-zones subcommand.
 	CanGetZones
+
+	// CanUseAzureAlias indicates the provider support the specific Azure_ALIAS records that only the Azure provider supports
+	CanUseAzureAlias
 )
 
 var providerCapabilities = map[string]map[Capability]bool{}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index fdf63599..83af8fda 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,30 +1,38 @@
 # cloud.google.com/go v0.41.1-0.20190703172311-335e9e09b93e
+## explicit
 cloud.google.com/go/compute/metadata
 # github.com/Azure/azure-sdk-for-go v39.1.0+incompatible
+## explicit
 github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns
 github.com/Azure/azure-sdk-for-go/version
 # github.com/Azure/go-autorest/autorest v0.9.4
+## explicit
 github.com/Azure/go-autorest/autorest
 github.com/Azure/go-autorest/autorest/azure
 # github.com/Azure/go-autorest/autorest/adal v0.8.1
 github.com/Azure/go-autorest/autorest/adal
 # github.com/Azure/go-autorest/autorest/azure/auth v0.4.2
+## explicit
 github.com/Azure/go-autorest/autorest/azure/auth
 # github.com/Azure/go-autorest/autorest/azure/cli v0.3.1
 github.com/Azure/go-autorest/autorest/azure/cli
 # github.com/Azure/go-autorest/autorest/date v0.2.0
 github.com/Azure/go-autorest/autorest/date
 # github.com/Azure/go-autorest/autorest/to v0.3.0
+## explicit
 github.com/Azure/go-autorest/autorest/to
 # github.com/Azure/go-autorest/logger v0.1.0
 github.com/Azure/go-autorest/logger
 # github.com/Azure/go-autorest/tracing v0.5.0
 github.com/Azure/go-autorest/tracing
 # github.com/DisposaBoy/JsonConfigReader v0.0.0-20171218180944-5ea4d0ddac55
+## explicit
 github.com/DisposaBoy/JsonConfigReader
 # github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
+## explicit
 github.com/TomOnTime/utfutil
 # github.com/aws/aws-sdk-go v1.29.1
+## explicit
 github.com/aws/aws-sdk-go/aws
 github.com/aws/aws-sdk-go/aws/awserr
 github.com/aws/aws-sdk-go/aws/awsutil
@@ -64,24 +72,32 @@ github.com/aws/aws-sdk-go/service/route53domains
 github.com/aws/aws-sdk-go/service/sts
 github.com/aws/aws-sdk-go/service/sts/stsiface
 # github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8
+## explicit
 github.com/billputer/go-namecheap
 # github.com/cenkalti/backoff v2.1.1+incompatible
+## explicit
 github.com/cenkalti/backoff
 # github.com/cpuguy83/go-md2man/v2 v2.0.0
+## explicit
 github.com/cpuguy83/go-md2man/v2/md2man
 # github.com/davecgh/go-spew v1.1.1
 github.com/davecgh/go-spew/spew
 # github.com/dgrijalva/jwt-go v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible
+## explicit
 github.com/dgrijalva/jwt-go
 # github.com/digitalocean/godo v1.30.0
+## explicit
 github.com/digitalocean/godo
 # github.com/dimchansky/utfbom v1.1.0
 github.com/dimchansky/utfbom
 # github.com/dnsimple/dnsimple-go v0.31.0
+## explicit
 github.com/dnsimple/dnsimple-go/dnsimple
 # github.com/exoscale/egoscale v0.23.0
+## explicit
 github.com/exoscale/egoscale
 # github.com/go-acme/lego v2.7.2+incompatible
+## explicit
 github.com/go-acme/lego/acme
 github.com/go-acme/lego/acme/api
 github.com/go-acme/lego/acme/api/internal/nonces
@@ -99,6 +115,7 @@ github.com/go-acme/lego/log
 github.com/go-acme/lego/platform/wait
 github.com/go-acme/lego/registration
 # github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe
+## explicit
 github.com/gobwas/glob
 github.com/gobwas/glob/compiler
 github.com/gobwas/glob/match
@@ -110,6 +127,7 @@ github.com/gobwas/glob/util/strings
 # github.com/gofrs/uuid v3.2.0+incompatible
 github.com/gofrs/uuid
 # github.com/golang/protobuf v1.3.3
+## explicit
 github.com/golang/protobuf/proto
 github.com/golang/protobuf/ptypes
 github.com/golang/protobuf/ptypes/any
@@ -118,16 +136,20 @@ github.com/golang/protobuf/ptypes/timestamp
 # github.com/golang/snappy v0.0.1
 github.com/golang/snappy
 # github.com/google/go-github v17.0.0+incompatible
+## explicit
 github.com/google/go-github/github
 # github.com/google/go-querystring v1.0.1-0.20190318165438-c8c88dbee036
+## explicit
 github.com/google/go-querystring/query
 # github.com/google/uuid v1.1.2-0.20190416172445-c2e93f3ae59f
+## explicit
 github.com/google/uuid
 # github.com/googleapis/gax-go/v2 v2.0.5
 github.com/googleapis/gax-go/v2
 # github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1
 github.com/gopherjs/gopherjs/js
 # github.com/gopherjs/jquery v0.0.0-20191017083323-73f4c7416038
+## explicit
 github.com/gopherjs/jquery
 # github.com/hashicorp/errwrap v1.0.0
 github.com/hashicorp/errwrap
@@ -156,6 +178,7 @@ github.com/hashicorp/hcl/json/parser
 github.com/hashicorp/hcl/json/scanner
 github.com/hashicorp/hcl/json/token
 # github.com/hashicorp/vault/api v1.0.4
+## explicit
 github.com/hashicorp/vault/api
 # github.com/hashicorp/vault/sdk v0.1.13
 github.com/hashicorp/vault/sdk/helper/compressutil
@@ -165,6 +188,7 @@ github.com/hashicorp/vault/sdk/helper/jsonutil
 github.com/hashicorp/vault/sdk/helper/parseutil
 github.com/hashicorp/vault/sdk/helper/strutil
 # github.com/hexonet/go-sdk v2.2.3+incompatible
+## explicit
 github.com/hexonet/go-sdk/apiclient
 github.com/hexonet/go-sdk/column
 github.com/hexonet/go-sdk/record
@@ -173,11 +197,19 @@ github.com/hexonet/go-sdk/responseparser
 github.com/hexonet/go-sdk/responsetemplate
 github.com/hexonet/go-sdk/responsetemplatemanager
 github.com/hexonet/go-sdk/socketconfig
+# github.com/jarcoal/httpmock v1.0.4
+## explicit
 # github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af
 github.com/jmespath/go-jmespath
 # github.com/kolo/xmlrpc v0.0.0-20150413191830-0826b98aaa29
+## explicit
 github.com/kolo/xmlrpc
+# github.com/kr/pretty v0.1.0
+## explicit
+# github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8
+## explicit
 # github.com/miekg/dns v1.1.27
+## explicit
 github.com/miekg/dns
 github.com/miekg/dns/dnsutil
 # github.com/mitchellh/go-homedir v1.1.0
@@ -185,12 +217,16 @@ github.com/mitchellh/go-homedir
 # github.com/mitchellh/mapstructure v1.1.2
 github.com/mitchellh/mapstructure
 # github.com/mjibson/esc v0.2.0
+## explicit
 github.com/mjibson/esc/embed
 # github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04
+## explicit
 github.com/namedotcom/go/namecom
 # github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
+## explicit
 github.com/ovh/go-ovh/ovh
 # github.com/philhug/opensrs-go v0.0.0-20171126225031-9dfa7433020d
+## explicit
 github.com/philhug/opensrs-go/opensrs
 # github.com/pierrec/lz4 v2.0.5+incompatible
 github.com/pierrec/lz4
@@ -200,6 +236,7 @@ github.com/pkg/errors
 # github.com/pmezard/go-difflib v1.0.0
 github.com/pmezard/go-difflib/difflib
 # github.com/prasmussen/gandi-api v0.0.0-20180224132202-58d3d4205661
+## explicit
 github.com/prasmussen/gandi-api/client
 github.com/prasmussen/gandi-api/domain
 github.com/prasmussen/gandi-api/domain/nameservers
@@ -212,8 +249,10 @@ github.com/prasmussen/gandi-api/live_dns/zone
 github.com/prasmussen/gandi-api/operation
 github.com/prasmussen/gandi-api/util
 # github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03
+## explicit
 github.com/renier/xmlrpc
 # github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff
+## explicit
 github.com/robertkrimen/otto
 github.com/robertkrimen/otto/ast
 github.com/robertkrimen/otto/dbg
@@ -228,7 +267,10 @@ github.com/russross/blackfriday/v2
 github.com/ryanuber/go-glob
 # github.com/shurcooL/sanitized_anchor_name v1.0.0
 github.com/shurcooL/sanitized_anchor_name
+# github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a
+## explicit
 # github.com/softlayer/softlayer-go v0.0.0-20170804160555-5e1c8cccc730
+## explicit
 github.com/softlayer/softlayer-go/config
 github.com/softlayer/softlayer-go/datatypes
 github.com/softlayer/softlayer-go/filter
@@ -236,23 +278,31 @@ github.com/softlayer/softlayer-go/services
 github.com/softlayer/softlayer-go/session
 github.com/softlayer/softlayer-go/sl
 # github.com/stretchr/testify v1.4.0
+## explicit
 github.com/stretchr/testify/assert
 # github.com/tdewolff/minify v2.3.6+incompatible
+## explicit
 github.com/tdewolff/minify
 github.com/tdewolff/minify/json
 # github.com/tdewolff/parse v2.3.4+incompatible
+## explicit
 github.com/tdewolff/parse
 github.com/tdewolff/parse/buffer
 github.com/tdewolff/parse/json
 github.com/tdewolff/parse/strconv
+# github.com/tdewolff/test v1.0.6
+## explicit
 # github.com/tiramiseb/go-gandi v0.0.0-20200227122905-7601fc3acbaa
+## explicit
 github.com/tiramiseb/go-gandi
 github.com/tiramiseb/go-gandi/domain
 github.com/tiramiseb/go-gandi/internal/client
 github.com/tiramiseb/go-gandi/livedns
 # github.com/urfave/cli/v2 v2.1.1
+## explicit
 github.com/urfave/cli/v2
 # github.com/vultr/govultr v0.2.0
+## explicit
 github.com/vultr/govultr
 # go.opencensus.io v0.22.0
 go.opencensus.io
@@ -272,6 +322,7 @@ go.opencensus.io/trace/internal
 go.opencensus.io/trace/propagation
 go.opencensus.io/trace/tracestate
 # golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
+## explicit
 golang.org/x/crypto/ed25519
 golang.org/x/crypto/ed25519/internal/edwards25519
 golang.org/x/crypto/ocsp
@@ -279,9 +330,11 @@ golang.org/x/crypto/pbkdf2
 golang.org/x/crypto/pkcs12
 golang.org/x/crypto/pkcs12/internal/rc2
 # golang.org/x/mod v0.2.0
+## explicit
 golang.org/x/mod/module
 golang.org/x/mod/semver
 # golang.org/x/net v0.0.0-20200222125558-5a598a2470a0
+## explicit
 golang.org/x/net/bpf
 golang.org/x/net/context
 golang.org/x/net/context/ctxhttp
@@ -297,12 +350,14 @@ golang.org/x/net/ipv6
 golang.org/x/net/publicsuffix
 golang.org/x/net/trace
 # golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
+## explicit
 golang.org/x/oauth2
 golang.org/x/oauth2/google
 golang.org/x/oauth2/internal
 golang.org/x/oauth2/jws
 golang.org/x/oauth2/jwt
 # golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c
+## explicit
 golang.org/x/sys/unix
 golang.org/x/sys/windows
 # golang.org/x/text v0.3.2
@@ -320,15 +375,18 @@ golang.org/x/text/unicode/norm
 # golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
 golang.org/x/time/rate
 # golang.org/x/tools v0.0.0-20200212213243-2ee7536ab1cc
+## explicit
 golang.org/x/tools/go/ast/astutil
 golang.org/x/tools/imports
 golang.org/x/tools/internal/fastwalk
 golang.org/x/tools/internal/gopathwalk
 golang.org/x/tools/internal/imports
 # golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
+## explicit
 golang.org/x/xerrors
 golang.org/x/xerrors/internal
 # google.golang.org/api v0.17.0
+## explicit
 google.golang.org/api/dns/v1
 google.golang.org/api/googleapi
 google.golang.org/api/googleapi/transport
@@ -339,6 +397,7 @@ google.golang.org/api/option
 google.golang.org/api/transport/http
 google.golang.org/api/transport/http/internal/propagation
 # google.golang.org/appengine v1.6.5
+## explicit
 google.golang.org/appengine
 google.golang.org/appengine/internal
 google.golang.org/appengine/internal/app_identity
@@ -388,9 +447,13 @@ google.golang.org/grpc/serviceconfig
 google.golang.org/grpc/stats
 google.golang.org/grpc/status
 google.golang.org/grpc/tap
+# gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
+## explicit
 # gopkg.in/ini.v1 v1.42.0
+## explicit
 gopkg.in/ini.v1
 # gopkg.in/ns1/ns1-go.v2 v2.0.0-20170502175150-c563826f4cbe
+## explicit
 gopkg.in/ns1/ns1-go.v2/rest
 gopkg.in/ns1/ns1-go.v2/rest/model/account
 gopkg.in/ns1/ns1-go.v2/rest/model/data
@@ -398,6 +461,7 @@ gopkg.in/ns1/ns1-go.v2/rest/model/dns
 gopkg.in/ns1/ns1-go.v2/rest/model/filter
 gopkg.in/ns1/ns1-go.v2/rest/model/monitor
 # gopkg.in/sourcemap.v1 v1.0.5
+## explicit
 gopkg.in/sourcemap.v1
 gopkg.in/sourcemap.v1/base64vlq
 # gopkg.in/square/go-jose.v2 v2.3.1
@@ -406,4 +470,5 @@ gopkg.in/square/go-jose.v2/cipher
 gopkg.in/square/go-jose.v2/json
 gopkg.in/square/go-jose.v2/jwt
 # gopkg.in/yaml.v2 v2.2.8
+## explicit
 gopkg.in/yaml.v2


Test patch:
diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go
index 70de69fc..d72c5603 100644
--- a/integrationTest/integration_test.go
+++ b/integrationTest/integration_test.go
@@ -26,10 +26,10 @@ func init() {
 	flag.Parse()
 }
 
-func getProvider(t *testing.T) (providers.DNSServiceProvider, string, map[int]bool) {
+func getProvider(t *testing.T) (providers.DNSServiceProvider, string, map[int]bool, map[string]string) {
 	if *providerToRun == "" {
 		t.Log("No provider specified with -provider")
-		return nil, "", nil
+		return nil, "", nil, nil
 	}
 	jsons, err := config.LoadProviderConfigs("providers.json")
 	if err != nil {
@@ -53,19 +53,19 @@ func getProvider(t *testing.T) (providers.DNSServiceProvider, string, map[int]bo
 				fails[i] = true
 			}
 		}
-		return provider, cfg["domain"], fails
+		return provider, cfg["domain"], fails, cfg
 	}
 	t.Fatalf("Provider %s not found", *providerToRun)
-	return nil, "", nil
+	return nil, "", nil, nil
 }
 
 func TestDNSProviders(t *testing.T) {
-	provider, domain, fails := getProvider(t)
+	provider, domain, fails, cfg := getProvider(t)
 	if provider == nil {
 		return
 	}
 	t.Run(fmt.Sprintf("%s", domain), func(t *testing.T) {
-		runTests(t, provider, domain, fails)
+		runTests(t, provider, domain, fails, cfg)
 	})
 
 }
@@ -84,7 +84,7 @@ func getDomainConfigWithNameservers(t *testing.T, prv providers.DNSServiceProvid
 	return dc
 }
 
-func runTests(t *testing.T, prv providers.DNSServiceProvider, domainName string, knownFailures map[int]bool) {
+func runTests(t *testing.T, prv providers.DNSServiceProvider, domainName string, knownFailures map[int]bool, origConfig map[string]string) {
 	dc := getDomainConfigWithNameservers(t, prv, domainName)
 	// run tests one at a time
 	end := *endIdx
@@ -107,11 +107,22 @@ func runTests(t *testing.T, prv providers.DNSServiceProvider, domainName string,
 			for _, r := range tst.Records {
 				rc := models.RecordConfig(*r)
 				if strings.Contains(rc.GetTargetField(), "**current-domain**") {
-					rc.SetTarget(strings.Replace(rc.GetTargetField(), "**current-domain**", domainName, 1) + ".")
+					_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**current-domain**", domainName, 1) + ".")
+				}
+				if strings.Contains(rc.GetTargetField(), "**current-domain-no-trailing**") {
+					_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**current-domain-no-trailing**", domainName, 1))
 				}
 				if strings.Contains(rc.GetLabelFQDN(), "**current-domain**") {
 					rc.SetLabelFromFQDN(strings.Replace(rc.GetLabelFQDN(), "**current-domain**", domainName, 1), domainName)
 				}
+				if providers.ProviderHasCapability(*providerToRun, providers.CanUseAzureAlias) {
+					if strings.Contains(rc.GetTargetField(), "**subscription-id**") {
+						_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**subscription-id**", origConfig["SubscriptionID"], 1))
+					}
+					if strings.Contains(rc.GetTargetField(), "**resource-group**") {
+						_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**resource-group**", origConfig["ResourceGroup"], 1))
+					}
+				}
 				dom.Records = append(dom.Records, &rc)
 			}
 			dom.IgnoredLabels = tst.IgnoredLabels
@@ -156,7 +167,7 @@ func runTests(t *testing.T, prv providers.DNSServiceProvider, domainName string,
 }
 
 func TestDualProviders(t *testing.T) {
-	p, domain, _ := getProvider(t)
+	p, domain, _, _ := getProvider(t)
 	if p == nil {
 		return
 	}
@@ -239,6 +250,14 @@ func r53alias(name, aliasType, target string) *rec {
 	return r
 }
 
+func azureAlias(name, aliasType, target string) *rec {
+	r := makeRec(name, target, "AZURE_ALIAS")
+	r.AzureAlias = map[string]string{
+		"type": aliasType,
+	}
+	return r
+}
+
 func ns(name, target string) *rec {
 	return makeRec(name, target, "NS")
 }
@@ -473,9 +492,7 @@ func makeTests(t *testing.T) []*TestCase {
 				sshfp("@", 1, 1, "66c7d5540b7d75a1fb4c84febfa178ad99bdd67c")),
 			tc("SSHFP change algorithm",
 				sshfp("@", 2, 1, "66c7d5540b7d75a1fb4c84febfa178ad99bdd67c")),
-			tc("SSHFP change type",
-				sshfp("@", 2, 2, "66c7d5540b7d75a1fb4c84febfa178ad99bdd67c")),
-			tc("SSHFP change fingerprint",
+			tc("SSHFP change fingerprint and type",
 				sshfp("@", 2, 2, "745a635bc46a397a5c4f21d437483005bcc40d7511ff15fbfafe913a081559bc")),
 			tc("SSHFP Delete one"),
 			tc("SSHFP add many records",
@@ -644,6 +661,20 @@ func makeTests(t *testing.T) []*TestCase {
 			tc("Update 1200 records", manyA("rec%04d", "1.2.3.5", 1200)...),
 		)
 	}
+	// AZURE_ALIAS
+	if !providers.ProviderHasCapability(*providerToRun, providers.CanUseAzureAlias) {
+		t.Log("Skipping AZURE_ALIAS Tests because provider does not support them")
+	} else {
+		t.Log("SubscriptionID: ")
+		tests = append(tests, tc("Empty"),
+			tc("create dependent A records", a("foo.a", "1.2.3.4"), a("quux.a", "2.3.4.5")),
+			tc("ALIAS to A record in same zone", a("foo.a", "1.2.3.4"), a("quux.a", "2.3.4.5"), azureAlias("bar.a", "A", "/subscriptions/**subscription-id**/resourceGroups/**resource-group**/providers/Microsoft.Network/dnszones/**current-domain-no-trailing**/A/foo.a")),
+			tc("change it", a("foo.a", "1.2.3.4"), a("quux.a", "2.3.4.5"), azureAlias("bar.a", "A", "/subscriptions/**subscription-id**/resourceGroups/**resource-group**/providers/Microsoft.Network/dnszones/**current-domain-no-trailing**/A/quux.a")),
+			tc("create dependent CNAME records", cname("foo.cname", "google.com"), cname("quux.cname", "google2.com")),
+			tc("ALIAS to CNAME record in same zone", cname("foo.cname", "google.com"), cname("quux.cname", "google2.com"), azureAlias("bar", "CNAME", "/subscriptions/**subscription-id**/resourceGroups/**resource-group**/providers/Microsoft.Network/dnszones/**current-domain-no-trailing**/CNAME/foo.cname")),
+			tc("change it", cname("foo.cname", "google.com"), cname("quux.cname", "google2.com"), azureAlias("bar.cname", "CNAME", "/subscriptions/**subscription-id**/resourceGroups/**resource-group**/providers/Microsoft.Network/dnszones/**current-domain-no-trailing**/CNAME/quux.cname")),
+		)
+	}
 
 	// Empty last
 	tests = append(tests, tc("Empty"))
diff --git a/integrationTest/providers.json b/integrationTest/providers.json
index dd40cb35..441ec4f6 100644
--- a/integrationTest/providers.json
+++ b/integrationTest/providers.json
@@ -2,7 +2,7 @@
   "ACTIVEDIRECTORY_PS": {
     "ADServer": "$AD_SERVER",
     "domain": "$AD_DOMAIN",
-    "knownFailures": "29,30,31,32,33,34,35,38,39,40,41,48,49,51,52,53"
+    "knownFailures": "29,30,31,32,33,34,35,38,39,40,41,48,50,51,52"
   },
   "AZURE_DNS": {
     "SubscriptionID": "$AZURE_SUBSCRIPTION_ID",
@@ -19,7 +19,7 @@
     "auth-id": "$CLOUDNS_AUTH_ID",
     "auth-password": "$CLOUDNS_AUTH_PASSWORD",
     "domain": "$CLOUDNS_DOMAIN",
-    "knownFailures": "53"
+    "knownFailures": "52"
   },
 
   "CLOUDFLAREAPI_OLD": {
@@ -125,7 +125,6 @@
     "app-key": "$OVH_APP_KEY",
     "app-secret-key": "$OVH_APP_SECRET_KEY",
     "consumer-key": "$OVH_CONSUMER_KEY",
-    "domain": "$OVH_DOMAIN",
-    "knownFailures": "49"
+    "domain": "$OVH_DOMAIN"
   }
 }
diff --git a/pkg/js/parse_tests/026-azure-alias.js b/pkg/js/parse_tests/026-azure-alias.js
new file mode 100644
index 00000000..06ea367d
--- /dev/null
+++ b/pkg/js/parse_tests/026-azure-alias.js
@@ -0,0 +1,5 @@
+D("foo.com", "none",
+  AZURE_ALIAS("atest", "A", "foo.com."),
+  AZURE_ALIAS("aaaatest", "AAAA", "foo.com."),
+  AZURE_ALIAS("cnametest", "CNAME", "foo.com.")
+);
\ No newline at end of file
diff --git a/pkg/js/parse_tests/026-azure-alias.json b/pkg/js/parse_tests/026-azure-alias.json
new file mode 100644
index 00000000..22bed19f
--- /dev/null
+++ b/pkg/js/parse_tests/026-azure-alias.json
@@ -0,0 +1,37 @@
+{
+  "registrars": [],
+  "dns_providers": [],
+  "domains": [
+    {
+      "name": "foo.com",
+      "registrar": "none",
+      "dnsProviders": {},
+      "records": [
+        {
+          "type": "AZURE_ALIAS",
+          "name": "atest",
+          "target": "foo.com.",
+          "azure_alias": {
+            "type": "A"
+          }
+        },
+        {
+          "type": "AZURE_ALIAS",
+          "name": "aaaatest",
+          "target": "foo.com.",
+          "azure_alias": {
+            "type": "AAAA"
+          }
+        },
+        {
+          "type": "AZURE_ALIAS",
+          "name": "cnametest",
+          "target": "foo.com.",
+          "azure_alias": {
+            "type": "CNAME"
+          }
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file