%3CLINGO-SUB%20id%3D%22lingo-sub-1282268%22%20slang%3D%22en-US%22%3EUsing%20Azure%20Automation%20with%20Multiple%20Tenants%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1282268%22%20slang%3D%22en-US%22%3E%3CP%3EWelcome%20to%20a%20tale%20from%20the%20lab%20of%20Jon%20Warnken%20a%20Premier%20Field%20Engineer.%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI%20have%20been%20working%20with%20%3CA%20href%3D%22https%3A%2F%2Fazure.microsoft.com%2Fservices%2Fautomation%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3EAzure%20Automation%3C%2FA%3E%20recently%2C%26nbsp%3B%20had%20a%20need%20for%20a%20runbook%20that%20required%20access%20in%20two%20different%20Azure%20Tenants.%20Because%20I%20had%20a%20virtual%20machine%20in%20one%20tenant%20(Tenant1)%20that%20is%20using%20a%20dynamic%20public%20IP%20address%20that%20is%20being%20used%20in%20a%20DNS%20zone%20record%20in%20another%20tenant%20(Tenant2)%2C%20I%20need%20to%20update%20the%20DNS%20record%20when%20the%20ip%20address%20changes.%20Now%20if%20the%20VM%20was%20in%20the%20same%20tenant%20as%20the%20dns%20zone%20this%20would%20be%20easy%20by%20making%20an%20alias%20record%20and%20map%20it%20to%20the%20public%20IP%20of%20the%20VM.%3C%2FP%3E%0A%3CP%3EA%20quick%20search%20found%20a%20couple%20of%20existing%20runbooks%20that%20work%20with%20in%20the%20same%20tenant.%20After%20updating%20the%20code%20to%20use%20parameters%20and%20the%20Az%20modules%20this%20is%20runbook%20code%20I%20am%20using%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3EParam(%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24RunAsAccount%20%3D%20%22AzureRunAsConnection%22%2C%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24SubscriptionId1%2C%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24SubscriptionId2%2C%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24vmresourcegroup%2C%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24vmipname%2C%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24DNSrecord%2C%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24DNSzone%2C%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24DNSresourcegroup%2C%0A%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%5BString%5D%24DNSrecordName%0A)%0A%24connectionName%20%3D%20%24RunAsAccount%0A%24servicePrincipalConnection%20%3D%20Get-AutomationConnection%20-Name%20%24connectionName%0AConnect-AzAccount%20-ServicePrincipal%20-TenantId%20%24servicePrincipalConnection.TenantId%20-ApplicationId%20%24servicePrincipalConnection.ApplicationId%20-CertificateThumbprint%20%24servicePrincipalConnection.CertificateThumbprint%20%7C%20out-null%0AWrite-Output%20%22Connecting%20to%20subscription%3A%20%20%24SubscriptionId1%22%0ASelect-AzSubscription%20-SubscriptionId%20%24SubscriptionId1%20%7C%20out-null%0A%24VMIP%20%3D%20(Get-AzPublicIpAddress%20-ResourceGroupName%20%24vmresourcegroup%20-Name%20%24vmipname).IpAddress%0AWrite-Output%20%22VM%20Public%20Ip%20is%20%24VMIP%22%0A%24DynDNS%20%3D%20%24DNSrecord%0A%23Get%20IP%20based%20on%20the%20Domain%20Name%0A%5Bstring%5D%24MyDynamicIP%20%3D%20(%5BSystem.Net.DNS%5D%3A%3AGetHostAddresses(%24DynDNS)).IPAddressToString%0AWrite-Output%20%22%24DynDNS%20resolves%20to%20%24MyDynamicIP%22%0AIf(%24VMIP%20-ne%20%24MyDynamicIP)%7B%0A%20%20%20%20Write-Output%20%22Connecting%20to%20subscription%3A%20%20%24SubscriptionId2%22%0A%20%20%20%20Select-AzSubscription%20-SubscriptionId%20%24SubscriptionId2%20%7C%20out-null%0A%20%20%20%20%24zone%20%3D%20Get-AzDnsZone%20-Name%20%24DNSzone%20-ResourceGroupName%20%24DNSresourcegroup%0A%20%20%20%20%24dnsrs%20%3D%20Get-AzDnsRecordSet%20-Zone%20%24zone%20-Name%20%24DNSrecordName%20-RecordType%20A%0A%20%20%20%20%24DNSIP%20%3D%20(Get-AzDnsRecordSet%20-Zone%20%24zone%20-Name%20%24DNSrecordName%20-RecordType%20A).Records.Ipv4Address%0A%20%20%20%20Remove-AzDnsRecordConfig%20-RecordSet%20%24dnsrs%20-Ipv4Address%20%24DNSIP%0A%20%20%20%20Add-AzDnsRecordConfig%20-RecordSet%20%24dnsrs%20-Ipv4Address%20%24VMIP%0A%20%20%20%20Set-AzDnsRecordSet%20-RecordSet%20%24dnsrs%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EIf%20you%20would%20like%20to%20use%20this%20runbook%2C%20you%20will%20find%20it%20published%20on%20the%20%3CA%20href%3D%22https%3A%2F%2Fwww.powershellgallery.com%2Fpackages%2FUpdate-AzDNSRecordMultiSub%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noopener%20noreferrer%20noopener%20noreferrer%22%3EPowerShell%20Gallery%3C%2FA%3E%20and%20can%20be%20imported%20into%20your%20automation%20account.%3C%2FP%3E%0A%3CP%3EUp%20to%20this%20point%20I%20have%20been%20testing%20with%20in%20the%20same%20tenant%20and%20everything%20was%20working%20great.%20Because%20my%20automation%20account%20does%20not%20have%20any%20access%20in%20the%20other%20tenant%2C%20when%20I%20use%20a%20subscription%20id%20in%20Tenant2%20the%20runbook%20will%20fail.%20Now%20I%20had%20to%20figure%20out%20how%20to%20authenticate%20this%20runbook%20in%20my%20second%20tenant.%20I%20considered%20three%20different%20options%3A%3C%2FP%3E%0A%3CP%3E1%20%E2%80%93%20Create%20an%20account%20in%20Tenant2%3B%20store%20the%20password%20in%20a%20key%20vault%20in%20Tenant1%3B%20have%20the%20runbook%20retrieve%20the%20password%20and%20use%20it%20to%20authenticate%20when%20needed%20for%20actions%20in%20Tenant2.%3C%2FP%3E%0A%3CP%3E2%20%E2%80%93%20Create%20an%20account%20in%20Tenant1%3B%20add%20the%20account%20as%20a%20guest%20user%20in%20Tenant2%3B%20use%20the%20account%20to%20execute%20the%20runbook.%3C%2FP%3E%0A%3CP%3E3%20%E2%80%93%20Delegate%20the%20automation%20account%E2%80%99s%20run%20as%20account%20in%20Tenant%202.%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EOption%201%20worked%20when%20manually%20testing%20the%20code%20but%20ran%20into%20context%20and%20timing%20issues%20when%20executing%20via%20the%20automation%20account.%20This%20approach%20also%20has%20security%20and%20operational%20risks%20that%20would%20not%20make%20it%20acceptable%20for%20a%20production%20environment.%3C%2FP%3E%0A%3CP%3EOption%202%20worked%20when%20manually%20testing%20but%20still%20failed%20when%20executing%20in%20the%20context%20of%20the%20automation%20account.%3C%2FP%3E%0A%3CP%3EThe%20manual%20testing%20of%20the%20other%20options%20showed%20that%20it%20was%20possible%20to%20do%20automate%20this.%20It%20just%20needed%20to%20be%20delegated%20correctly.%20Enter%20%3CA%20href%3D%22https%3A%2F%2Fazure.microsoft.com%2Fen-us%2Fservices%2Fazure-lighthouse%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3EAzure%20Lighthouse%3C%2FA%3E%2C%20this%20is%20how%20partners%20provide%20managed%20services%20in%20Azure.%20But%20the%20functionality%20can%20also%20be%20used%20by%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Flighthouse%2Fconcepts%2Fenterprise%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Eenterprises%20with%20multiple%20tenants%3C%2FA%3E.%20I%20decided%20to%20onboard%20via%20an%20Azure%20Resource%20Manager%20template.%20The%20process%20and%20sample%20template%20are%20available%20at%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Flighthouse%2Fhow-to%2Fonboard-customer%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Flighthouse%2Fhow-to%2Fonboard-customer%3C%2FA%3E.%3C%2FP%3E%0A%3CP%3EThe%20only%20challenge%20I%20had%20was%20getting%20the%20principal%20id%20for%20my%20automation%20run%20as%20account.%20The%20run%20as%20account%20exists%20as%20an%20application%20and%20a%20managed%20application%20in%20Azure%20Active%20Directory.%20If%20you%20go%20the%20portal%20and%20look%20up%20the%20application%20you%20will%20see%20the%20Managed%20application%20instance.%3C%2FP%3E%0A%3CDIV%20id%3D%22tinyMceEditorJon%20Warnken_0%22%20class%3D%22mceNonEditable%20lia-copypaste-placeholder%22%3E%26nbsp%3B%3C%2FDIV%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-center%22%20image-alt%3D%22image.png%22%20style%3D%22width%3A%20624px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Fgxcuf89792.i.lithium.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F182214i92ACE1E2BF370D96%2Fimage-size%2Flarge%3Fv%3D1.0%26amp%3Bpx%3D999%22%20title%3D%22image.png%22%20alt%3D%22image.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3CSPAN%20style%3D%22font-family%3A%20inherit%3B%22%3E%26nbsp%3B%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%20Clicking%20on%20that%20link%20will%20allow%20you%20to%20discover%20the%20correct%20object%20id%20to%20use%20with%20your%20ARM%20template.%3C%2FP%3E%0A%3CDIV%20id%3D%22tinyMceEditorJon%20Warnken_1%22%20class%3D%22mceNonEditable%20lia-copypaste-placeholder%22%3E%26nbsp%3B%3C%2FDIV%3E%0A%3CP%3EArmed%20this%20and%20the%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Frole-based-access-control%2Fbuilt-in-roles%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Erole%20id%3C%2FA%3E%20you%20want%20to%20delegate%20complete%20the%20parameter%20json%20file%20and%20onboard%20the%20delegation%20in%20the%20target%20tenant.%20I%20used%20the%20resource%20group%20delegation%20template%20option%3C%2FP%3E%0A%3CDIV%20id%3D%22tinyMceEditorJon%20Warnken_2%22%20class%3D%22mceNonEditable%20lia-copypaste-placeholder%22%3E%26nbsp%3B%3C%2FDIV%3E%0A%3CP%3EAnd%20setup%20the%20parameter%20file%20to%20delegate%20a%20user%20with%20owner%20access%20and%20my%20automation%20account%20with%20dns%20zone%20contributor%20access.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3E%7B%0A%20%20%20%20%22%24schema%22%3A%20%22https%3A%2F%2Fschema.management.azure.com%2Fschemas%2F2015-01-01%2FdeploymentParameters.json%23%22%2C%0A%20%20%20%20%22contentVersion%22%3A%20%221.0.0.0%22%2C%0A%20%20%20%20%22parameters%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22mspOfferName%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22value%22%3A%20%22Fabrikam%20Managed%20Services%20-%20MrBoDean%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22rgName%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22value%22%3A%20%22Your%20resource%20group%20name%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22mspOfferDescription%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22value%22%3A%20%22Fabrikam%20Managed%20Services%20-%20MrBoDean%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22managedByTenantId%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22value%22%3A%20%22xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22authorizations%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22value%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22principalId%22%3A%20%22yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22roleDefinitionId%22%3A%20%228e3af657-a8ff-443c-a75c-2fe8c4bcb635%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22principalIdDisplayName%22%3A%20%22Your%20User%20Name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22principalId%22%3A%20%22cccccccc-cccc-cccc-cccc-cccccccccccc%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22roleDefinitionId%22%3A%20%22befefa01-2a29-4197-83a8-272ff33ce314%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22principalIdDisplayName%22%3A%20%22Your%20Automation%20Account%20User%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3ENow%20this%20runbook%20can%20check%20and%20update%20the%20DNS%20record%20in%20the%20remote%20tenant.%3C%2FP%3E%0A%3CDIV%20id%3D%22tinyMceEditorJon%20Warnken_3%22%20class%3D%22mceNonEditable%20lia-copypaste-placeholder%22%3E%26nbsp%3B%3C%2FDIV%3E%0A%3CP%3EHopefully%20you%20find%20this%20useful%20and%20happy%20automating.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EDisclaimer%3CBR%20%2F%3EThe%20sample%20scripts%20are%20not%20supported%20under%20any%20Microsoft%20standard%20support%20program%20or%20service.%20The%20sample%20scripts%20are%20provided%20AS%20IS%20without%20warranty%20of%20any%20kind.%20Microsoft%20further%20disclaims%20all%20implied%20warranties%20including%2C%20without%20limitation%2C%20any%20implied%20warranties%20of%20merchantability%20or%20of%20fitness%20for%20a%20particular%20purpose.%20The%20entire%20risk%20arising%20out%20of%20the%20use%20or%20performance%20of%20the%20sample%20scripts%20and%20documentation%20remains%20with%20you.%20In%20no%20event%20shall%20Microsoft%2C%20its%20authors%2C%20or%20anyone%20else%20involved%20in%20the%20creation%2C%20production%2C%20or%20delivery%20of%20the%20scripts%20be%20liable%20for%20any%20damages%20whatsoever%20(including%2C%20without%20limitation%2C%20damages%20for%20loss%20of%20business%20profits%2C%20business%20interruption%2C%20loss%20of%20business%20information%2C%20or%20other%20pecuniary%20loss)%20arising%20out%20of%20the%20use%20of%20or%20inability%20to%20use%20the%20sample%20scripts%20or%20documentation%2C%20even%20if%20Microsoft%20has%20been%20advised%20of%20the%20possibility%20of%20such%20damages.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-1282268%22%20slang%3D%22en-US%22%3E%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-center%22%20image-alt%3D%22AzLighthouse.png%22%20style%3D%22width%3A%20193px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Fgxcuf89792.i.lithium.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F182219i4B21812DBE4B3F5E%2Fimage-size%2Flarge%3Fv%3D1.0%26amp%3Bpx%3D999%22%20title%3D%22AzLighthouse.png%22%20alt%3D%22AzLighthouse.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EWelcome%20to%20a%20tale%20from%20the%20lab%20of%20Jon%20Warnken%20a%20Premier%20Field%20Engineer.%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI%20have%20been%20working%20with%20%26nbsp%3B%20recently%2C%26nbsp%3B%20had%20a%20need%20for%20a%20runbook%20that%20required%20access%20in%20two%20different%20Azure%20Tenants.%3C%2FP%3E%3C%2FLINGO-TEASER%3E%3CLINGO-LABS%20id%3D%22lingo-labs-1282268%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EJonathanWarnken%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E
Microsoft

Welcome to a tale from the lab of Jon Warnken a Premier Field Engineer. 

 

I have been working with Azure Automation recently,  had a need for a runbook that required access in two different Azure Tenants. Because I had a virtual machine in one tenant (Tenant1) that is using a dynamic public IP address that is being used in a DNS zone record in another tenant (Tenant2), I need to update the DNS record when the ip address changes. Now if the VM was in the same tenant as the dns zone this would be easy by making an alias record and map it to the public IP of the VM.

A quick search found a couple of existing runbooks that work with in the same tenant. After updating the code to use parameters and the Az modules this is runbook code I am using:

 

 

 

 

 

Param(
    [Parameter(Mandatory=$true)]
    [String]$RunAsAccount = "AzureRunAsConnection",
    [Parameter(Mandatory=$true)]
    [String]$SubscriptionId1,
    [Parameter(Mandatory=$true)]
    [String]$SubscriptionId2,
    [Parameter(Mandatory=$true)]
    [String]$vmresourcegroup,
    [Parameter(Mandatory=$true)]
    [String]$vmipname,
    [Parameter(Mandatory=$true)]
    [String]$DNSrecord,
    [Parameter(Mandatory=$true)]
    [String]$DNSzone,
    [Parameter(Mandatory=$true)]
    [String]$DNSresourcegroup,
    [Parameter(Mandatory=$true)]
    [String]$DNSrecordName
)
$connectionName = $RunAsAccount
$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName
Connect-AzAccount -ServicePrincipal -TenantId $servicePrincipalConnection.TenantId -ApplicationId $servicePrincipalConnection.ApplicationId -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint | out-null
Write-Output "Connecting to subscription:  $SubscriptionId1"
Select-AzSubscription -SubscriptionId $SubscriptionId1 | out-null
$VMIP = (Get-AzPublicIpAddress -ResourceGroupName $vmresourcegroup -Name $vmipname).IpAddress
Write-Output "VM Public Ip is $VMIP"
$DynDNS = $DNSrecord
#Get IP based on the Domain Name
[string]$MyDynamicIP = ([System.Net.DNS]::GetHostAddresses($DynDNS)).IPAddressToString
Write-Output "$DynDNS resolves to $MyDynamicIP"
If($VMIP -ne $MyDynamicIP){
    Write-Output "Connecting to subscription:  $SubscriptionId2"
    Select-AzSubscription -SubscriptionId $SubscriptionId2 | out-null
    $zone = Get-AzDnsZone -Name $DNSzone -ResourceGroupName $DNSresourcegroup
    $dnsrs = Get-AzDnsRecordSet -Zone $zone -Name $DNSrecordName -RecordType A
    $DNSIP = (Get-AzDnsRecordSet -Zone $zone -Name $DNSrecordName -RecordType A).Records.Ipv4Address
    Remove-AzDnsRecordConfig -RecordSet $dnsrs -Ipv4Address $DNSIP
    Add-AzDnsRecordConfig -RecordSet $dnsrs -Ipv4Address $VMIP
    Set-AzDnsRecordSet -RecordSet $dnsrs
}

 

 

 

 

 

 

If you would like to use this runbook, you will find it published on the PowerShell Gallery and can be imported into your automation account.

Up to this point I have been testing with in the same tenant and everything was working great. Because my automation account does not have any access in the other tenant, when I use a subscription id in Tenant2 the runbook will fail. Now I had to figure out how to authenticate this runbook in my second tenant. I considered three different options:

1 – Create an account in Tenant2; store the password in a key vault in Tenant1; have the runbook retrieve the password and use it to authenticate when needed for actions in Tenant2.

2 – Create an account in Tenant1; add the account as a guest user in Tenant2; use the account to execute the runbook.

3 – Delegate the automation account’s run as account in Tenant 2. 

 

Option 1 worked when manually testing the code but ran into context and timing issues when executing via the automation account. This approach also has security and operational risks that would not make it acceptable for a production environment.

Option 2 worked when manually testing but still failed when executing in the context of the automation account.

The manual testing of the other options showed that it was possible to do automate this. It just needed to be delegated correctly. Enter Azure Lighthouse, this is how partners provide managed services in Azure. But the functionality can also be used by enterprises with multiple tenants. I decided to onboard via an Azure Resource Manager template. The process and sample template are available at https://docs.microsoft.com/en-us/azure/lighthouse/how-to/onboard-customer.

The only challenge I had was getting the principal id for my automation run as account. The run as account exists as an application and a managed application in Azure Active Directory. If you go the portal and look up the application you will see the Managed application instance.

 

image.png

  

  Clicking on that link will allow you to discover the correct object id to use with your ARM template.

 

Armed this and the role id you want to delegate complete the parameter json file and onboard the delegation in the target tenant. I used the resource group delegation template option

 

And setup the parameter file to delegate a user with owner access and my automation account with dns zone contributor access.

 

 

 

 

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "mspOfferName": {
            "value": "Fabrikam Managed Services - MrBoDean"
        },
        "rgName": {
            "value": "Your resource group name"
        },
        "mspOfferDescription": {
            "value": "Fabrikam Managed Services - MrBoDean"
        },
        "managedByTenantId": {
            "value": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        },
        "authorizations": {
            "value": [
                {
                    "principalId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
                    "roleDefinitionId": "8e3af657-a8ff-443c-a75c-2fe8c4bcb635",
                    "principalIdDisplayName": "Your User Name"
                },
                {
                    "principalId": "cccccccc-cccc-cccc-cccc-cccccccccccc",
                    "roleDefinitionId": "befefa01-2a29-4197-83a8-272ff33ce314",
                    "principalIdDisplayName": "Your Automation Account User"
                }
            ]
        }
    }
}

 

 

 

 

Now this runbook can check and update the DNS record in the remote tenant.

 

Hopefully you find this useful and happy automating.

 

Disclaimer
The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.