Saturday, 7 August 2010

DNS config

Saw a thread on another blog and wanted to post a potential solution -

An xml doc like this -

<RoleData>

<Dns>
<Server Address="localhost" />
</Dns>

<!-- DnsType can be StandAlone or ADIntegrated -->
<!-- VIP types can be A or CNAME -->
<LoadBalancing DnsType="StandAlone">
<Zone Name="test.com">
<VIP Address="192.168.1.1" Record="address1" Type="A" />
<VIP Address="192.168.1.2" Record="address2" Type="A" />
<VIP Address="192.168.1.3" Record="address3" Type="A" />
<VIP Address="192.168.1.4" Record="address4" Type="A" />
<VIP Address="192.168.1.5" Record="address5" Type="A" />
<VIP Address="192.168.1.6" Record="address6" Type="A" />
<VIP Address="192.168.1.7" Record="address7" Type="A" />
</Zone>
<Zone Name="sophie.com">
<VIP Address="192.168.1.1" Record="address1" Type="A" />
<VIP Address="192.168.1.2" Record="address2" Type="A" />
<VIP Address="192.168.1.3" Record="address3" Type="A" />
<VIP Address="192.168.1.4" Record="address4" Type="A" />
<VIP Address="192.168.1.5" Record="address5" Type="A" />
<VIP Address="192.168.1.6" Record="address6" Type="A" />
<VIP Address="192.168.1.7" Record="address7" Type="A" />
<VIP Address="address7" Record="address8" Type="CNAME" />
</Zone>
</LoadBalancing>

</RoleData>

A script to process, only caters for A & Cname, should determine whether to update all DnsServers in list or just the primary if AdIntegrated zone.



######################################################################
#
# Setup-DnsEntries.ps1 [optional]-NoPrompt [optional]-Config
#
# This tool will create DNS records that are found in
# Dns.xml, this must live alongside the tool or specified at
# commandline.
#
######################################################################

############################# Params #################################
Param([Switch]$NoPrompt, [string]$Config)
######################################################################

########################### Functions ################################

function New-ARecord ([string]$Server, [string]$Zone, [string]$Record, [string]$Address)
{
$ErrorActionPreference = "SilentlyContinue"
if (!($Server) -or !($Zone) -or !($Record) -or !($Address))
{
throw "function New-ARecord called with incorrect parameters..."
return $false
}

$Expr = "dnscmd $($Server) /RecordAdd $($Zone) $($Record) A $($Address)"
$Return = Invoke-Expression $Expr

if ($LastExitCode -ne 0)
{
throw "An error occured during invocation of expression: $($Expr)"
return $false
}
else
{
return $true
}
}

function New-CnameRecord ([string]$Server, [string]$Zone, [string]$Record, [string]$Address)
{
$ErrorActionPreference = "SilentlyContinue"
if (!($Server) -or !($Zone) -or !($Record) -or !($Address))
{
throw "function New-CnameRecord called with incorrect parameters..."
return $false
}

$Expr = "dnscmd $($Server) /RecordAdd $($Zone) $($Record) CNAME $($Address + "." + $Zone)"
$Return = Invoke-Expression $Expr

if ($LastExitCode -ne 0)
{
throw "An error occured during invocation of expression: $($Expr)"
return $false
}
else
{
return $true
}
}

function New-Zone ([string]$Server, [string]$Zone, [string]$DnsType)
{
$ErrorActionPreference = "SilentlyContinue"
if (!($Zone) -or !($DnsType) -or !($DnsType))
{
throw "function New-Zone called with incorrect parameters..."
return $false
}

switch ($DnsType)
{
"AdIntegrated" {$Param = "/DsPrimary /DP /domain"}
"StandAlone" {$Param = "/Primary"}
}
$Expr = "dnscmd $($Server) /ZoneAdd $($Zone) $($Param)"
$Return = Invoke-Expression $Expr

if ($LastExitCode -ne 0)
{
throw "An error occured during invocation of expression: $($Expr)"
return $false
}
else
{
return $true
}
}

function Remove-ARecord ([string]$Server, [string]$Zone, [string]$Record, [string]$Address)
{
$ErrorActionPreference = "SilentlyContinue"
if (!($Zone) -or !($Record) -or !($Address))
{
throw "function Remove-ARecord called with incorrect parameters..."
return $false
}

$Expr = "dnscmd $($Server) /RecordDelete $($Zone) $($Record) A $($Address) /f"
$Return = Invoke-Expression $Expr

if ($LastExitCode -ne 0)
{
throw "An error occured during invocation of expression: $($Expr)"
return $false
}
else
{
return $true
}
}

function Remove-CnameRecord ([string]$Server, [string]$Zone, [string]$Record, [string]$Address)
{
$ErrorActionPreference = "SilentlyContinue"
if (!($Zone) -or !($Record) -or !($Address))
{
throw "function Remove-ARecord called with incorrect parameters..."
return $false
}

$Expr = "dnscmd $($Server) /RecordDelete $($Zone) $($Record) CNAME $($Address) /f"
$Return = Invoke-Expression $Expr

if ($LastExitCode -ne 0)
{
throw "An error occured during invocation of expression: $($Expr)"
return $false
}
else
{
return $true
}
}

function Send-Error ()
{
Write-Host "############################## Error ###############################"
$error | ForEach-Object {Write-Host $_}
Write-Host "####################################################################"
break
exit 999
}
######################################################################

############################ Variables ###############################
# --- clear error stack, set error preference
$error.Clear()
$ErrorActionPreference = "SilentlyContinue"
# --- handle if xml path passed in
if ($Config)
{
if (!(Test-Path $Config))
{
throw "Unable to locate: $($Config)"
Send-Error
}
else
{
[Xml]$Xml = Get-Content -Path $Config
if ($error.count -gt 0)
{
# --- malformed xml...
throw "Xml appears to be malformed..."
Send-Error
}
}
}
elseif (Test-Path $((Get-Location).Path + "\Dns.xml"))
{
[Xml]$Xml = Get-Content -Path $((Get-Location).Path + "\Dns.xml")
if ($error.count -gt 0)
{
# --- malformed xml...
throw "Xml appears to be malformed..."
Send-Error
}
}
else
{
throw "No config passed to script, and no default found..."
Send-Error
}
# --- Get Xml structures needed
$Servers = $Xml.RoleData.Dns.Server
$DnsType = $Xml.RoleData.LoadBalancing.DnsType
$Zones = $Xml.RoleData.LoadBalancing.Zone
if (!($Servers) -or !($DnsType) -or !($Zones))
{
throw "Xml didnt contain required structures..."
Send-Error
}
# --- switch DnsType to get serverlist
switch ($DnsType)
{
"StandAlone" {$DnsServers = $Servers}
"ADIntegrated" {$DnsServers = $Servers[0]}
}
######################################################################

############################### Main #################################

# --- Build nice object array
[Array]$ZoneObjs = @()
foreach ($Zone in $Zones)
{
$ZoneName = $Zone.Name
if (!($ZoneName))
{
throw "Zone node didnt contain name attribute..."
Send-Error
}
$ZoneObj = New-Object System.Object
Add-Member -MemberType "NoteProperty" -Name "ZoneName" -InputObject $ZoneObj -Value $ZoneName

[Array]$Records = @()
foreach ($Record in $Zone.VIP)
{
if (!($Record.Address) -or !($Record.Record) -or !($Record.Type))
{
throw "Record didnt contain required attributes..."
}
$RecordObj = New-Object System.Object
Add-Member -MemberType "NoteProperty" -Name "Address" -InputObject $RecordObj -Value $Record.Address
Add-Member -MemberType "NoteProperty" -Name "Record" -InputObject $RecordObj -Value $Record.Record
Add-Member -MemberType "NoteProperty" -Name "Type" -InputObject $RecordObj -Value $Record.Type
$Records += $RecordObj
}
Add-Member -MemberType "NoteProperty" -Name "Records" -InputObject $ZoneObj -Value $Records

# --- Add to root array
$ZoneObjs += $ZoneObj
}

# --- Send errors if there are any
if ($error.Count -gt 0)
{
Send-Error
}

# --- Loop and build worklist
[Array]$NewZones = @()
[Array]$NewRecords = @()
[Array]$Updates = @()
foreach ($Server in $DnsServers)
{
foreach ($Zone in $ZoneObjs)
{
# --- Check zone exists
$Expr = "dnscmd /EnumZones"
$Return = Invoke-Expression $Expr
$Return = $Return | Where-Object {$_.Contains($Zone.ZoneName)}
if (!($Return))
{
$TempZObj = New-Object System.Object
Add-Member -MemberType "NoteProperty" -Name "Zone" -InputObject $TempZObj -Value $Zone.ZoneName
Add-Member -MemberType "NoteProperty" -Name "Server" -InputObject $TempZObj -Value $Server.Address
$NewZones += $TempZObj
}
# --- check records
foreach ($Record in $Zone.Records)
{
$TempObj = New-Object System.Object
$Expr = "dnscmd $($Server.Address) /EnumRecords $($Zone.ZoneName) $($Record.Record)"
$Return = Invoke-Expression $Expr
# --- new record please!
if ($Return[1].Length -eq 0 -or $Return[1].Contains("DNS Server failed to enumerate records for node"))
{
Add-Member -MemberType "NoteProperty" -Name "Zone" -InputObject $TempObj -Value $Zone.ZoneName
Add-Member -MemberType "NoteProperty" -Name "Record" -InputObject $TempObj -Value $Record.Record
Add-Member -MemberType "NoteProperty" -Name "Address" -InputObject $TempObj -Value $Record.Address
Add-Member -MemberType "NoteProperty" -Name "Type" -InputObject $TempObj -Value $Record.Type
Add-Member -MemberType "NoteProperty" -Name "Server" -InputObject $TempObj -Value $Server.Address
$NewRecords += $TempObj
}
else
{
# --- record exists, make sure ip is right!
$IpReturn = $Return[1].Split("`t")[1]
switch ($Record.Type)
{
"A" {$Match = $Record.Address}
"CNAME" {$Match = $Record.Address + "." + $Zone.ZoneName + "."}
default {$Match = $Record.Address}
}
if (!($IpReturn -eq $Match))
{
Add-Member -MemberType "NoteProperty" -Name "Zone" -InputObject $TempObj -Value $Zone.ZoneName
Add-Member -MemberType "NoteProperty" -Name "Record" -InputObject $TempObj -Value $Record.Record
Add-Member -MemberType "NoteProperty" -Name "OldAddress" -InputObject $TempObj -Value $IpReturn
Add-Member -MemberType "NoteProperty" -Name "NewAddress" -InputObject $TempObj -Value $Record.Address
Add-Member -MemberType "NoteProperty" -Name "Type" -InputObject $TempObj -Value $Record.Type
Add-Member -MemberType "NoteProperty" -Name "Server" -InputObject $TempObj -Value $Server.Address
$Updates += $TempObj
}
}
}
}
}

# --- some feedback and choice
Write-Host "`t#################################################"
Write-Host "`n`tThis will execute the following actions:"
Write-Host "`n`tNew Zones -"
$NewZones | ForEach-Object {Write-Host "`t`tName:$($_.Zone) Server:$($_.Server)"}
Write-Host "`n`tNew Records -"
$NewRecords | ForEach-Object {Write-Host "`t`tName:$($_.Record) Zone:$($_.Zone) Address:$($_.Address) Type:$($_.Type)"}
Write-Host "`n`tUpdate Records -"
$Updates | ForEach-Object {Write-Host "`t`tName:$($_.Record) Zone:$($_.Zone) OldAddress:$($_.OldAddress) NewAddress:$($_.NewAddress) Type:$($_.Type)"}
Write-Host "`n"
Write-Host "`t#################################################"

# --- prompt and work
if (!($NoPrompt.isPresent))
{
do
{
$Choice = Read-Host -Prompt "Do you want to continue? [y/n]"
}
while ($Choice -ne "y" -and $Choice -ne "n")

if ($Choice -ieq "y")
{
# --- Action new zones
foreach ($Zone in $NewZones)
{
if (!(New-Zone -Server $Zone.Server -Zone $Zone.Zone -DnsType $DnsType))
{
Send-Error
}
}

# --- action new records
foreach ($Entry in $NewRecords)
{
switch ($Entry.Type)
{
"CNAME" {
if (!(New-CnameRecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.Address))
{
Send-Error
}
}
"A" {
if (!(New-ARecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.Address))
{
Send-Error
}
}
}
}

# --- action updates
foreach ($Entry in $Updates)
{
switch ($Entry.Type)
{
"CNAME" {
if (!(Remove-CnameRecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.OldAddress))
{
Send-Error
}
if (!(New-CnameRecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.NewAddress))
{
Send-Error
}
}
"A" {
if (!(Remove-ARecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.OldAddress))
{
Send-Error
}
if (!(New-ARecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.NewAddress))
{
Send-Error
}
}
}
}
}
else
{
Write-Host "Exiting..."
}
}
else
{
# --- Action new zones
foreach ($Zone in $NewZones)
{
if (!(New-Zone -Server $Zone.Server -Zone $Zone.Zone -DnsType $DnsType))
{
Send-Error
}
}

# --- action new records
foreach ($Entry in $NewRecords)
{
switch ($Entry.Type)
{
"CNAME" {
if (!(New-CnameRecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.Address))
{
Send-Error
}
}
"A" {
if (!(New-ARecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.Address))
{
Send-Error
}
}
}
}

# --- action updates
foreach ($Entry in $Updates)
{
switch ($Entry.Type)
{
"CNAME" {
if (!(Remove-CnameRecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.OldAddress))
{
Send-Error
}
if (!(New-CnameRecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.NewAddress))
{
Send-Error
}
}
"A" {
if (!(Remove-ARecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.OldAddress))
{
Send-Error
}
if (!(New-ARecord -Server $Entry.Server -Zone $Entry.Zone -Record $Entry.Record -Address $Entry.NewAddress))
{
Send-Error
}
}
}
}
}
######################################################################

Saturday, 12 June 2010

A quote

"Grau, theurer Freund, ist alle Theorie,
Und greun des lebens goldner Baum"

'Grey is all theory, green life's golden tree'

First post....

Hi, thanks for taking a look at my blog!
I'm hoping to keep posting up things which generally take my interest and things which I hope other people may find useful.

I work in IT and quite often find myself in situations where even Google cant provide me with an answer so I expect that will be the majority of the content.