PowerInfoblox.psm1
function Get-IPAddressRangeInformation { <# .SYNOPSIS Provides information about IP Address range .DESCRIPTION Provides information about IP Address range .PARAMETER Network Network in form of IP/NetworkLength (e.g. 10.2.10.0/24') .PARAMETER IPAddress IP Address to use .PARAMETER NetworkLength Network length to use .PARAMETER CIDRObject CIDRObject to use .EXAMPLE $CidrObject = @{ Ip = '10.2.10.0' NetworkLength = 24 } Get-IPAddressRangeInformation -CIDRObject $CidrObject | Format-Table .EXAMPLE Get-IPAddressRangeInformation -Network '10.2.10.0/24' | Format-Table .EXAMPLE Get-IPAddressRangeInformation -IPAddress '10.2.10.0' -NetworkLength 24 | Format-Table .NOTES General notes #> [cmdletBinding(DefaultParameterSetName = 'Network')] param( [Parameter(ParameterSetName = 'Network', Mandatory)][string] $Network, [Parameter(ParameterSetName = 'IPAddress', Mandatory)][string] $IPAddress, [Parameter(ParameterSetName = 'IPAddress', Mandatory)][int] $NetworkLength, [Parameter(ParameterSetName = 'CIDR', Mandatory)][psobject] $CIDRObject ) $IPv4Regex = '(?:(?:0?0?\d|0?[1-9]\d|1\d\d|2[0-5][0-5]|2[0-4]\d)\.){3}(?:0?0?\d|0?[1-9]\d|1\d\d|2[0-5][0-5]|2[0-4]\d)' if ($Network) { $CIDRObject = @{ Ip = $Network.Split('/')[0] NetworkLength = $Network.Split('/')[1] } } elseif ($IPAddress -and $NetworkLength) { $CIDRObject = @{ Ip = $IPAddress NetworkLength = $NetworkLength } } elseif ($CIDRObject) { } else { Write-Error "Get-IPAddressRangeInformation - Invalid parameters specified" return } $o = [ordered] @{} $o.IP = [string] $CIDRObject.IP $o.BinaryIP = Convert-IPToBinary $o.IP if (-not $o.BinaryIP) { return } $o.NetworkLength = [int32] $CIDRObject.NetworkLength $o.SubnetMask = Convert-BinaryToIP ('1' * $o.NetworkLength).PadRight(32, '0') $o.BinarySubnetMask = ('1' * $o.NetworkLength).PadRight(32, '0') $o.BinaryNetworkAddress = $o.BinaryIP.SubString(0, $o.NetworkLength).PadRight(32, '0') if ($Contains) { if ($Contains -match "\A${IPv4Regex}\z") { return Test-IPIsInNetwork $Contains $o.BinaryNetworkAddress $o.BinaryNetworkAddress.SubString(0, $o.NetworkLength).PadRight(32, '1') } else { Write-Error "Get-IPAddressRangeInformation - Invalid IPv4 address specified with -Contains" return } } $o.NetworkAddress = Convert-BinaryToIP $o.BinaryNetworkAddress if ($o.NetworkLength -eq 32 -or $o.NetworkLength -eq 31) { $o.HostMin = $o.IP } else { $o.HostMin = Convert-BinaryToIP ([System.Convert]::ToString(([System.Convert]::ToInt64($o.BinaryNetworkAddress, 2) + 1), 2)).PadLeft(32, '0') } [string] $BinaryBroadcastIP = $o.BinaryNetworkAddress.SubString(0, $o.NetworkLength).PadRight(32, '1') $o.BinaryBroadcast = $BinaryBroadcastIP [int64] $DecimalHostMax = [System.Convert]::ToInt64($BinaryBroadcastIP, 2) - 1 [string] $BinaryHostMax = [System.Convert]::ToString($DecimalHostMax, 2).PadLeft(32, '0') $o.HostMax = Convert-BinaryToIP $BinaryHostMax $o.TotalHosts = [int64][System.Convert]::ToString(([System.Convert]::ToInt64($BinaryBroadcastIP, 2) - [System.Convert]::ToInt64($o.BinaryNetworkAddress, 2) + 1)) $o.UsableHosts = $o.TotalHosts - 2 if ($o.NetworkLength -eq 32) { $o.Broadcast = $Null $o.UsableHosts = [int64] 1 $o.TotalHosts = [int64] 1 $o.HostMax = $o.IP } elseif ($o.NetworkLength -eq 31) { $o.Broadcast = $Null $o.UsableHosts = [int64] 2 $o.TotalHosts = [int64] 2 [int64] $DecimalHostMax2 = [System.Convert]::ToInt64($BinaryBroadcastIP, 2) [string] $BinaryHostMax2 = [System.Convert]::ToString($DecimalHostMax2, 2).PadLeft(32, '0') $o.HostMax = Convert-BinaryToIP $BinaryHostMax2 } elseif ($o.NetworkLength -eq 30) { $o.UsableHosts = [int64] 2 $o.TotalHosts = [int64] 4 $o.Broadcast = Convert-BinaryToIP $BinaryBroadcastIP } else { $o.Broadcast = Convert-BinaryToIP $BinaryBroadcastIP } if ($Enumerate) { $IPRange = @(Get-IPRange $o.BinaryNetworkAddress $o.BinaryNetworkAddress.SubString(0, $o.NetworkLength).PadRight(32, '1')) if ((31, 32) -notcontains $o.NetworkLength ) { $IPRange = $IPRange[1..($IPRange.Count - 1)] $IPRange = $IPRange[0..($IPRange.Count - 2)] } $o.IPEnumerated = $IPRange } else { $o.IPEnumerated = @() } [PSCustomObject]$o } function Join-UriQuery { <# .SYNOPSIS Provides ability to join two Url paths together including advanced querying .DESCRIPTION Provides ability to join two Url paths together including advanced querying which is useful for RestAPI/GraphApi calls .PARAMETER BaseUri Primary Url to merge .PARAMETER RelativeOrAbsoluteUri Additional path to merge with primary url (optional) .PARAMETER QueryParameter Parameters and their values in form of hashtable .PARAMETER EscapeUriString If set, will escape the url string .EXAMPLE Join-UriQuery -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' -QueryParameter @{ page = 1 per_page = 20 search = 'SearchString' } .EXAMPLE Join-UriQuery -BaseUri 'https://evotec.xyz/wp-json/wp/v2/posts' -QueryParameter @{ page = 1 per_page = 20 search = 'SearchString' } .EXAMPLE Join-UriQuery -BaseUri 'https://evotec.xyz' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' .NOTES General notes #> [alias('Join-UrlQuery')] [CmdletBinding()] param ( [parameter(Mandatory)][uri] $BaseUri, [parameter(Mandatory = $false)][uri] $RelativeOrAbsoluteUri, [Parameter()][System.Collections.IDictionary] $QueryParameter, [alias('EscapeUrlString')][switch] $EscapeUriString ) Begin { Add-Type -AssemblyName System.Web } Process { if ($BaseUri -and $RelativeOrAbsoluteUri) { $Url = Join-Uri -BaseUri $BaseUri -RelativeOrAbsoluteUri $RelativeOrAbsoluteUri } else { $Url = $BaseUri } if ($QueryParameter) { $Collection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) foreach ($key in $QueryParameter.Keys) { $Collection.Add($key, $QueryParameter.$key) } } $uriRequest = [System.UriBuilder] $Url if ($Collection) { $uriRequest.Query = $Collection.ToString() } if (-not $EscapeUriString) { $uriRequest.Uri.AbsoluteUri } else { [System.Uri]::EscapeUriString($uriRequest.Uri.AbsoluteUri) } } } function Remove-EmptyValue { [alias('Remove-EmptyValues')] [CmdletBinding()] param( [alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable, [string[]] $ExcludeParameter, [switch] $Recursive, [int] $Rerun, [switch] $DoNotRemoveNull, [switch] $DoNotRemoveEmpty, [switch] $DoNotRemoveEmptyArray, [switch] $DoNotRemoveEmptyDictionary ) foreach ($Key in [string[]] $Hashtable.Keys) { if ($Key -notin $ExcludeParameter) { if ($Recursive) { if ($Hashtable[$Key] -is [System.Collections.IDictionary]) { if ($Hashtable[$Key].Count -eq 0) { if (-not $DoNotRemoveEmptyDictionary) { $Hashtable.Remove($Key) } } else { Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } } if ($Rerun) { for ($i = 0; $i -lt $Rerun; $i++) { Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive } } } function Select-Properties { <# .SYNOPSIS Allows for easy selecting property names from one or multiple objects .DESCRIPTION Allows for easy selecting property names from one or multiple objects. This is especially useful with using AllProperties parameter where we want to make sure to get all properties from all objects. .PARAMETER Objects One or more objects .PARAMETER Property Properties to include .PARAMETER ExcludeProperty Properties to exclude .PARAMETER AllProperties All unique properties from all objects .PARAMETER PropertyNameReplacement Default property name when object has no properties .EXAMPLE $Object1 = [PSCustomobject] @{ Name1 = '1' Name2 = '3' Name3 = '5' } $Object2 = [PSCustomobject] @{ Name4 = '2' Name5 = '6' Name6 = '7' } Select-Properties -Objects $Object1, $Object2 -AllProperties #OR: $Object1, $Object2 | Select-Properties -AllProperties -ExcludeProperty Name6 -Property Name3 .EXAMPLE $Object3 = [Ordered] @{ Name1 = '1' Name2 = '3' Name3 = '5' } $Object4 = [Ordered] @{ Name4 = '2' Name5 = '6' Name6 = '7' } Select-Properties -Objects $Object3, $Object4 -AllProperties $Object3, $Object4 | Select-Properties -AllProperties .NOTES General notes #> [CmdLetBinding()] param( [Array][Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] $Objects, [string[]] $Property, [string[]] $ExcludeProperty, [switch] $AllProperties, [string] $PropertyNameReplacement = '*' ) Begin { function Select-Unique { [CmdLetBinding()] param( [System.Collections.IList] $Object ) [Array] $CleanedList = foreach ($O in $Object) { if ($null -ne $O) { $O } } $New = $CleanedList.ToLower() | Select-Object -Unique $Selected = foreach ($_ in $New) { $Index = $Object.ToLower().IndexOf($_) if ($Index -ne -1) { $Object[$Index] } } $Selected } $ObjectsList = [System.Collections.Generic.List[Object]]::new() } Process { foreach ($Object in $Objects) { $ObjectsList.Add($Object) } } End { if ($ObjectsList.Count -eq 0) { Write-Warning 'Select-Properties - Unable to process. Objects count equals 0.' return } if ($ObjectsList[0] -is [System.Collections.IDictionary]) { if ($AllProperties) { [Array] $All = foreach ($_ in $ObjectsList) { $_.Keys } $FirstObjectProperties = Select-Unique -Object $All } else { $FirstObjectProperties = $ObjectsList[0].Keys } if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) { $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($Property -contains $_ -and $ExcludeProperty -notcontains $_) { $_ continue } } } elseif ($Property.Count -gt 0) { $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($Property -contains $_) { $_ continue } } } elseif ($ExcludeProperty.Count -gt 0) { $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($ExcludeProperty -notcontains $_) { $_ continue } } } } elseif ($ObjectsList[0].GetType().Name -match 'bool|byte|char|datetime|decimal|double|ExcelHyperLink|float|int|long|sbyte|short|string|timespan|uint|ulong|URI|ushort') { $FirstObjectProperties = $PropertyNameReplacement } else { if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property $Property -ExcludeProperty $ExcludeProperty } elseif ($Property.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property $Property } elseif ($ExcludeProperty.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property '*' -ExcludeProperty $ExcludeProperty } if ($AllProperties) { [Array] $All = foreach ($_ in $ObjectsList) { $ListProperties = $_.PSObject.Properties.Name if ($null -ne $ListProperties) { $ListProperties } } $FirstObjectProperties = Select-Unique -Object $All } else { $FirstObjectProperties = $ObjectsList[0].PSObject.Properties.Name } } $FirstObjectProperties } } function Write-Color { <# .SYNOPSIS Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options. .DESCRIPTION Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options. It provides: - Easy manipulation of colors, - Logging output to file (log) - Nice formatting options out of the box. - Ability to use aliases for parameters .PARAMETER Text Text to display on screen and write to log file if specified. Accepts an array of strings. .PARAMETER Color Color of the text. Accepts an array of colors. If more than one color is specified it will loop through colors for each string. If there are more strings than colors it will start from the beginning. Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White .PARAMETER BackGroundColor Color of the background. Accepts an array of colors. If more than one color is specified it will loop through colors for each string. If there are more strings than colors it will start from the beginning. Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White .PARAMETER StartTab Number of tabs to add before text. Default is 0. .PARAMETER LinesBefore Number of empty lines before text. Default is 0. .PARAMETER LinesAfter Number of empty lines after text. Default is 0. .PARAMETER StartSpaces Number of spaces to add before text. Default is 0. .PARAMETER LogFile Path to log file. If not specified no log file will be created. .PARAMETER DateTimeFormat Custom date and time format string. Default is yyyy-MM-dd HH:mm:ss .PARAMETER LogTime If set to $true it will add time to log file. Default is $true. .PARAMETER LogRetry Number of retries to write to log file, in case it can't write to it for some reason, before skipping. Default is 2. .PARAMETER Encoding Encoding of the log file. Default is Unicode. .PARAMETER ShowTime Switch to add time to console output. Default is not set. .PARAMETER NoNewLine Switch to not add new line at the end of the output. Default is not set. .PARAMETER NoConsoleOutput Switch to not output to console. Default all output goes to console. .EXAMPLE Write-Color -Text "Red ", "Green ", "Yellow " -Color Red,Green,Yellow .EXAMPLE Write-Color -Text "This is text in Green ", "followed by red ", "and then we have Magenta... ", "isn't it fun? ", "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan .EXAMPLE Write-Color -Text "This is text in Green ", "followed by red ", "and then we have Magenta... ", "isn't it fun? ", "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1 .EXAMPLE Write-Color "1. ", "Option 1" -Color Yellow, Green Write-Color "2. ", "Option 2" -Color Yellow, Green Write-Color "3. ", "Option 3" -Color Yellow, Green Write-Color "4. ", "Option 4" -Color Yellow, Green Write-Color "9. ", "Press 9 to exit" -Color Yellow, Gray -LinesBefore 1 .EXAMPLE Write-Color -LinesBefore 2 -Text "This little ","message is ", "written to log ", "file as well." ` -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" -TimeFormat "yyyy-MM-dd HH:mm:ss" Write-Color -Text "This can get ","handy if ", "want to display things, and log actions to file ", "at the same time." ` -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" .EXAMPLE Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow Write-Color -t "my text" -c yellow -b green Write-Color -text "my text" -c red .EXAMPLE Write-Color -Text "Testuję czy się ładnie zapisze, czy będą problemy" -Encoding unicode -LogFile 'C:\temp\testinggg.txt' -Color Red -NoConsoleOutput .NOTES Understanding Custom date and time format strings: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings Project support: https://github.com/EvotecIT/PSWriteColor Original idea: Josh (https://stackoverflow.com/users/81769/josh) #> [alias('Write-Colour')] [CmdletBinding()] param ( [alias ('T')] [String[]]$Text, [alias ('C', 'ForegroundColor', 'FGC')] [ConsoleColor[]]$Color = [ConsoleColor]::White, [alias ('B', 'BGC')] [ConsoleColor[]]$BackGroundColor = $null, [alias ('Indent')][int] $StartTab = 0, [int] $LinesBefore = 0, [int] $LinesAfter = 0, [int] $StartSpaces = 0, [alias ('L')] [string] $LogFile = '', [Alias('DateFormat', 'TimeFormat')][string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss', [alias ('LogTimeStamp')][bool] $LogTime = $true, [int] $LogRetry = 2, [ValidateSet('unknown', 'string', 'unicode', 'bigendianunicode', 'utf8', 'utf7', 'utf32', 'ascii', 'default', 'oem')][string]$Encoding = 'Unicode', [switch] $ShowTime, [switch] $NoNewLine, [alias('HideConsole')][switch] $NoConsoleOutput ) if (-not $NoConsoleOutput) { $DefaultColor = $Color[0] if ($null -ne $BackGroundColor -and $BackGroundColor.Count -ne $Color.Count) { Write-Error "Colors, BackGroundColors parameters count doesn't match. Terminated." return } if ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host -Object "`n" -NoNewline } } # Add empty line before if ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host -Object "`t" -NoNewline } } # Add TABS before text if ($StartSpaces -ne 0) { for ($i = 0; $i -lt $StartSpaces; $i++) { Write-Host -Object ' ' -NoNewline } } # Add SPACES before text if ($ShowTime) { Write-Host -Object "[$([datetime]::Now.ToString($DateTimeFormat))] " -NoNewline } # Add Time before output if ($Text.Count -ne 0) { if ($Color.Count -ge $Text.Count) { # the real deal coloring if ($null -eq $BackGroundColor) { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline } } else { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline } } } else { if ($null -eq $BackGroundColor) { for ($i = 0; $i -lt $Color.Length ; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline } for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -NoNewline } } else { for ($i = 0; $i -lt $Color.Length ; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline } for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackGroundColor[0] -NoNewline } } } } if ($NoNewLine -eq $true) { Write-Host -NoNewline } else { Write-Host } # Support for no new line if ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host -Object "`n" -NoNewline } } # Add empty line after } if ($Text.Count -and $LogFile) { # Save to file $TextToFile = "" for ($i = 0; $i -lt $Text.Length; $i++) { $TextToFile += $Text[$i] } $Saved = $false $Retry = 0 Do { $Retry++ try { if ($LogTime) { "[$([datetime]::Now.ToString($DateTimeFormat))] $TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false } else { "$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false } $Saved = $true } catch { if ($Saved -eq $false -and $Retry -eq $LogRetry) { Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Tried ($Retry/$LogRetry))" } else { Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Retrying... ($Retry/$LogRetry)" } } } Until ($Saved -eq $true -or $Retry -ge $LogRetry) } } function Convert-BinaryToIP { [cmdletBinding()] param( [string] $Binary ) $Binary = $Binary -replace '\s+' if ($Binary.Length % 8) { Write-Warning -Message "Convert-BinaryToIP - Binary string '$Binary' is not evenly divisible by 8." return $Null } [int] $NumberOfBytes = $Binary.Length / 8 $Bytes = @(foreach ($i in 0..($NumberOfBytes - 1)) { try { [System.Convert]::ToByte($Binary.Substring(($i * 8), 8), 2) } catch { Write-Warning -Message "Convert-BinaryToIP - Error converting '$Binary' to bytes. `$i was $i." return $Null } }) return $Bytes -join '.' } function Convert-IPToBinary { [cmdletBinding()] param( [string] $IP ) $IPv4Regex = '(?:(?:0?0?\d|0?[1-9]\d|1\d\d|2[0-5][0-5]|2[0-4]\d)\.){3}(?:0?0?\d|0?[1-9]\d|1\d\d|2[0-5][0-5]|2[0-4]\d)' $IP = $IP.Trim() if ($IP -match "\A${IPv4Regex}\z") { try { return ($IP.Split('.') | ForEach-Object { [System.Convert]::ToString([byte] $_, 2).PadLeft(8, '0') }) -join '' } catch { Write-Warning -Message "Convert-IPToBinary - Error converting '$IP' to a binary string: $_" return $Null } } else { Write-Warning -Message "Convert-IPToBinary - Invalid IP detected: '$IP'. Conversion failed." return $Null } } function Get-IPRange { [cmdletBinding()] param( [string] $StartBinary, [string] $EndBinary ) [int64] $StartInt = [System.Convert]::ToInt64($StartBinary, 2) [int64] $EndInt = [System.Convert]::ToInt64($EndBinary, 2) for ($BinaryIP = $StartInt; $BinaryIP -le $EndInt; $BinaryIP++) { Convert-BinaryToIP ([System.Convert]::ToString($BinaryIP, 2).PadLeft(32, '0')) } } function Join-Uri { <# .SYNOPSIS Provides ability to join two Url paths together .DESCRIPTION Provides ability to join two Url paths together .PARAMETER BaseUri Primary Url to merge .PARAMETER RelativeOrAbsoluteUri Additional path to merge with primary url .EXAMPLE Join-Uri 'https://evotec.xyz/' '/wp-json/wp/v2/posts' .EXAMPLE Join-Uri 'https://evotec.xyz/' 'wp-json/wp/v2/posts' .EXAMPLE Join-Uri -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' .EXAMPLE Join-Uri -BaseUri 'https://evotec.xyz/test/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' .NOTES General notes #> [alias('Join-Url')] [cmdletBinding()] param( [parameter(Mandatory)][uri] $BaseUri, [parameter(Mandatory)][uri] $RelativeOrAbsoluteUri ) return ($BaseUri.OriginalString.TrimEnd('/') + "/" + $RelativeOrAbsoluteUri.OriginalString.TrimStart('/')) } function Test-IPIsInNetwork { [cmdletBinding()] param( [string] $IP, [string] $StartBinary, [string] $EndBinary ) $TestIPBinary = Convert-IPToBinary $IP [int64] $TestIPInt64 = [System.Convert]::ToInt64($TestIPBinary, 2) [int64] $StartInt64 = [System.Convert]::ToInt64($StartBinary, 2) [int64] $EndInt64 = [System.Convert]::ToInt64($EndBinary, 2) if ($TestIPInt64 -ge $StartInt64 -and $TestIPInt64 -le $EndInt64) { return $True } else { return $False } } function Convert-IpAddressToPtrString { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$IPAddress ) # Split the IP address into its octets $octets = $IPAddress -split "\." # Reverse the octets [array]::Reverse($octets) # Join the reversed octets with dots and append the standard PTR suffix $ptrString = ($octets -join ".") + ".in-addr.arpa" $ptrString } function Get-FieldsFromSchema { [CmdletBinding()] param( [PSCustomobject] $Schema, [string] $SchemaObject ) if (-not $Script:InfobloxSchemaFields) { $Script:InfobloxSchemaFields = [ordered] @{} } if ($Script:InfobloxSchemaFields[$SchemaObject]) { $Script:InfobloxSchemaFields[$SchemaObject] } else { if (-not $Schema) { $Schema = Get-InfobloxSchema -Object $SchemaObject } if ($Schema -and $Schema.fields.name) { $FilteredFields = foreach ($Field in $Schema.fields) { if ($Field.supports -like "*r*") { $Field.Name } } $Script:InfobloxSchemaFields[$SchemaObject] = ($FilteredFields -join ',') $Script:InfobloxSchemaFields[$SchemaObject] } else { Write-Warning -Message "Get-FieldsFromSchema - Failed to fetch schema for record type 'allrecords'. Using defaults" } } } function Hide-SelfSignedCerts { [cmdletbinding()] param( ) if ($PSVersionTable.PSVersion.Major -gt 5) { #Write-Warning -Message "Hide-SelfSignedCerts - This function is only supported in PowerShell 6 and later" $Script:InfobloxConfiguration['SkipCertificateValidation'] = $true return } try { Add-Type -TypeDefinition @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = [TrustAllCertsPolicy]::new() } catch { Write-Warning -Message "Hide-SelfSignedCerts - Error when trying to hide self-signed certificates: $($_.Exception.Message)" } } function New-WebSession { [cmdletbinding()] param( [System.Collections.IDictionary]$Cookies, [Uri]$For ) $newSession = [Microsoft.PowerShell.Commands.WebRequestSession]::new() foreach ($entry in $Cookies.GetEnumerator()) { $cookie = [System.Net.Cookie]::new($entry.Name, $entry.Value) if ($For) { $newSession.Cookies.Add([uri]::new($For, '/'), $cookie) } else { $newSession.Cookies.Add($cookie) } } $newSession } function Select-ObjectByProperty { [CmdletBinding()] param( [Parameter(Position = 0, ValueFromPipeline)][Array] $Object, [System.Collections.IDictionary] $AddObject, [alias('FirstProperty')][Parameter()][string[]] $FirstProperties, [alias('LastProperty')][Parameter()][string[]] $LastProperties, [string[]] $AllProperties ) process { foreach ($O in $Object) { # If we have an object, we can get the properties from it # we assume user can provide AllProperties instead of current object properties $Properties = if ($AllProperties) { $AllProperties } else { $O.PSObject.Properties.Name } $ReturnObject = [ordered] @{} foreach ($Property in $FirstProperties) { if ($Properties -contains $Property) { $ReturnObject[$Property] = $O.$Property } } if ($AddObject) { foreach ($Property in $AddObject.Keys) { $ReturnObject[$Property] = $AddObject[$Property] } } foreach ($Property in $Properties) { if ($Property -notin $LastProperties -and $Property -notin $FirstProperties) { $ReturnObject[$Property] = $O.$Property } } foreach ($Property in $LastProperties) { if ($Properties -contains $Property) { $ReturnObject[$Property] = $O.$Property } } [pscustomobject]$ReturnObject } } } function Add-InfobloxDHCPRange { <# .SYNOPSIS Adds a DHCP range to Infoblox. .DESCRIPTION This function adds a DHCP range to Infoblox. It requires an established connection to an Infoblox server, which can be done using the Connect-Infoblox function. .PARAMETER StartAddress The starting IP address of the DHCP range. This parameter is mandatory. .PARAMETER EndAddress The ending IP address of the DHCP range. This parameter is mandatory. .PARAMETER Name The name of the DHCP range. .PARAMETER Comment A comment for the DHCP range. .PARAMETER NetworkView The network view in which the DHCP range will be added. The default is 'default'. .PARAMETER MSServer The Microsoft server to which the DHCP range will be added. .PARAMETER ReturnOutput If this switch is present, the function will return the output of the operation. .PARAMETER ExtensibleAttribute An extensible attribute to be added to the DHCP range. .PARAMETER FailoverAssociation The failover association for the DHCP range. .PARAMETER Options An array of options to be added to the DHCP range. .PARAMETER ServerAssociationType The server association type for the DHCP range. The possible values are 'MEMBER', 'MS_FAILOVER', 'NONE', 'MS_SERVER', 'FAILOVER'. .PARAMETER Exclude An array of IP addresses or IP address ranges to be excluded from the DHCP range. .PARAMETER AlwaysUpdateDns If this switch is present, DNS will always be updated for the DHCP range. .PARAMETER Disable If this switch is present, the DHCP range will be disabled. .EXAMPLE Add-InfobloxDHCPRange -StartAddress '192.168.1.100' -EndAddress '192.168.1.200' -Name 'DHCP Range 1' -Comment 'This is a DHCP range.' Adds a DHCP range from 192.168.1.100 to 192.168.1.200 with the name 'DHCP Range 1' and a comment 'This is a DHCP range.'. .EXAMPLE Add-InfobloxDHCPRange -StartAddress '10.22.41.15' -EndAddress '10.22.41.30' Adds a reserved range from 10.22.41.15 to 10.22.41.30 .EXAMPLE $addInfobloxDHCPRangeSplat = @{ StartAddress = '10.22.41.51' EndAddress = '10.22.41.60' Verbose = $true MSServer = 'dhcp2016.evotec.pl' Name = 'DHCP Range Me?' ServerAssociationType = 'MS_SERVER' } Add-InfobloxDHCPRange @addInfobloxDHCPRangeSplat .EXAMPLE $addInfobloxDHCPRangeSplat = @{ StartAddress = '10.22.41.70' EndAddress = '10.22.41.90' Verbose = $true MSServer = 'dhcp2019.evotec.pl' Name = 'DHCP Range Me2?' ServerAssociationType = 'MS_SERVER' Exclude = '10.22.41.75-10.22.41.79' } Add-InfobloxDHCPRange @addInfobloxDHCPRangeSplat .EXAMPLE $addInfobloxDHCPRangeSplat = @{ StartAddress = '10.10.12.5' EndAddress = '10.10.12.10' Options = @( New-InfobloxOption -Name "dhcp-lease-time" -Number 51 -UseOption -Value '86400' -VendorClass 'DHCP' New-InfobloxOption -Name "domain-name-servers" -Number 6 -UseOption -Value '192.168.0.15' -VendorClass 'DHCP' New-InfobloxOption -Name 'routers' -Number 3 -UseOption -Value '192.168.11.12' -VendorClass 'DHCP' New-InfobloxOption -Name 'time-servers' -Number 4 -UseOption -Value '11' -VendorClass 'DHCP' ) Verbose = $true } Add-InfobloxDHCPRange @addInfobloxDHCPRangeSplat .NOTES You must first connect to an Infoblox server using Connect-Infoblox before running this function. Please note that when using MSServer parameter you need to provide a valid server name that is already added to Infoblox, and it also needs to be part of Members in Add-InfobloxNetwork. #> [CmdletBinding()] param( [Parameter(Mandatory)][string] $StartAddress, [Parameter(Mandatory)][string] $EndAddress, [string] $Name, [string] $Comment, [string] $NetworkView = 'default', [string] $MSServer, [switch] $ReturnOutput, [System.Collections.IDictionary] $ExtensinbleAttribute, [Array] $Options, [alias('failover_association')][string] $FailoverAssociation, [ValidateSet('MEMBER', 'MS_FAILOVER', 'NONE', 'MS_SERVER', 'FAILOVER')] [string] $ServerAssociationType, [Array] $Exclude, [switch] $AlwaysUpdateDns, [switch] $Disable ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Add-InfobloxDHCPRange - You must first connect to an Infoblox server using Connect-Infoblox' return } $Body = [ordered] @{ "start_addr" = $StartAddress "end_addr" = $EndAddress "comment" = $Comment "network_view" = $NetworkView } if ($Name) { $Body["name"] = $Name } if ($ServerAssociationType) { $Body["server_association_type"] = $ServerAssociationType } if ($MSServer) { $Body["ms_server"] = [PSCustomObject] @{ "_struct" = "msdhcpserver" "ipv4addr" = $MSServer } } if ($ExtensinbleAttribute) { $Body["extattrs"] = [ordered] @{} foreach ($Key in $ExtensinbleAttribute.Keys) { if ($ExtensinbleAttribute[$Key] -is [System.Collections.IDictionary]) { $Body["extattrs"][$Key] = $ExtensinbleAttribute[$Key] } else { $Body["extattrs"][$Key] = @{ value = $ExtensinbleAttribute[$Key] } } } } if ($Options) { $Body["options"] = @( foreach ($Option in $Options) { $Option } ) } if ($FailoverAssociation) { $Body["failover_association"] = $FailoverAssociation } if ($Exclude) { $Body["exclude"] = @( foreach ($ExcludeItem in $Exclude) { if ($ExcludeItem -is [string]) { if ($ExcludeItem -like "*-*") { $ExcludeItem = $ExcludeItem -split '-' $ExcludeItem = @{ StartAddress = $ExcludeItem[0] EndAddress = $ExcludeItem[1] } } else { $ExcludeItem = @{ StartAddress = $ExcludeItem EndAddress = $ExcludeItem } } } [ordered] @{ "start_address" = $ExcludeItem.StartAddress "end_address" = $ExcludeItem.EndAddress } } ) } if ($PSBoundParameters.ContainsKey('AlwaysUpdateDns')) { $Body["always_update_dns"] = $AlwaysUpdateDns.IsPresent } if ($PSBoundParameters.ContainsKey('Disable')) { $Body["disable"] = $Disable.IsPresent } Remove-EmptyValue -Hashtable $Body $invokeInfobloxQuerySplat = @{ RelativeUri = "range" Method = 'POST' Body = $Body } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat if ($Output) { Write-Verbose -Message "Add-InfobloxDHCPRange - $Output" if ($ReturnOutput) { $Output } } } function Add-InfoBloxDNSRecord { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Name Parameter description .PARAMETER IPAddress Parameter description .PARAMETER CanonicalName Parameter description .PARAMETER PtrName Parameter description .PARAMETER Type Parameter description .EXAMPLE Add-InfoBloxDNSRecord -Name 'Test' -IPv4Address '10.10.10.10' -Type 'A' .EXAMPLE Add-InfoBloxDNSRecord -Name 'Test' -IPv4Address '10.10.10.10' -Type 'HOST' .EXAMPLE Add-InfoBloxDNSRecord -Name 'Test' -CanonicalName 'test2.mcdonalds.com' -Type 'CNAME' .NOTES General notes #> [CmdletBinding(SupportsShouldProcess)] param( [string] $Name, [Alias('IPv4Address', 'IPv6Address')][string] $IPAddress, [string] $CanonicalName, [string] $PtrName, [string] $Text, [parameter(Mandatory)][ValidateSet( 'A', #'AAAA', 'CNAME', 'HOST', 'PTR' #'DName', #'DNSKEY', 'DS', 'Host', 'host_ipv4addr', 'host_ipv6addr', #'LBDN', 'MX', 'NAPTR', 'NS', 'NSEC', #'NSEC3', 'NSEC3PARAM', 'PTR', 'RRSIG', 'SRV', 'TXT' )] [string] $Type ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Add-InfoBloxDNSRecord - You must first connect to an Infoblox server using Connect-Infoblox' return } # Lets convert it to lowercase, since Infoblox is case sensitive $Type = $Type.ToLower() if ($Type -eq 'A') { Write-Verbose -Message "Add-InfoBloxDNSRecord - Adding $Type record $Name with IPAddress: '$IPAddress'" if ($Name -and $IPAddress) { $Body = [ordered] @{ name = $Name.ToLower() ipv4addr = $IPAddress } } else { if ($ErrorActionPreference -eq 'Stop') { throw "Add-InfoBloxDNSRecord - 'Name' and 'IPAddress' are required for $Type record" } Write-Warning -Message "'Name' and 'IPAddress' are required for $Type record" return } } elseif ($Type -eq 'CNAME') { Write-Verbose -Message "Add-InfoBloxDNSRecord - Adding $Type record $Name with IPAddress: '$IPAddress'" if ($Name -and $CanonicalName) { $Body = [ordered] @{ name = $Name.ToLower() canonical = $CanonicalName.ToLower() } } else { if ($ErrorActionPreference -eq 'Stop') { throw "'Name' and 'CanonicalName' are required for $Type record" } Write-Warning -Message "Add-InfoBloxDNSRecord - 'Name' and 'CanonicalName' are required for $Type record" return } } elseif ($Type -eq 'AAAA') { Write-Verbose -Message "Add-InfoBloxDNSRecord - Adding $Type record $Name with IPAddress: '$IPAddress'" if ($Name -and $IPAddress) { $Body = [ordered] @{ name = $Name.ToLower() ipv6addr = $IPAddress } } else { if ($ErrorActionPreference -eq 'Stop') { throw "'Name' and 'IPAddress' are required for $Type record" } Write-Warning -Message "Add-InfoBloxDNSRecord - 'Name' and 'IPAddress' are required for $Type record" return } } elseif ($Type -eq 'HOST') { Write-Verbose -Message "Add-InfoBloxDNSRecord - Adding $Type record $Name with IPAddress: '$IPAddress'" if ($Name -and $IPAddress) { $Body = [ordered] @{ name = $Name.ToLower() ipv4addrs = @( @{ ipv4addr = $IPAddress } ) } } else { if ($ErrorActionPreference -eq 'Stop') { throw "'Name' and 'IPAddress' are required for '$Type' record" } Write-Warning -Message "Add-InfoBloxDNSRecord - 'Name' and 'IPAddress' are required for '$Type' record" return } } elseif ($Type -eq 'PTR') { Write-Verbose -Message "Add-InfoBloxDNSRecord - Adding $Type record $Name with IPAddress: '$IPAddress'" if ($Name -and $IPAddress -and $PtrName) { $Body = [ordered] @{ name = $Name.ToLower() ptrdname = $PtrName.ToLower() ipv4addr = $IPAddress } } else { if ($ErrorActionPreference -eq 'Stop') { throw "'Name' and 'IPAddress' and 'PtrName' are required for '$Type' record" } Write-Warning -Message "Add-InfoBloxDNSRecord - 'Name' and 'IPAddress' and 'PtrName' are required for '$Type' record" return } } elseif ($Type -eq 'TXT') { Write-Verbose -Message "Add-InfoBloxDNSRecord - Adding $Type record $Name with TEXT: '$Text'" if ($Name -and $IPAddress) { $Body = [ordered] @{ name = $Name.ToLower() text = $Text.ToLower() } } else { if ($ErrorActionPreference -eq 'Stop') { throw "'Name' and 'Text' are required for '$Type' record" } Write-Warning -Message "Add-InfoBloxDNSRecord - 'Name' and 'Text' are required for '$Type' record" return } } else { # won't trigger, but lets leave it like that if ($ErrorActionPreference -eq 'Stop') { throw "Add-InfoBloxDNSRecord - Type $Type not supported" } Write-Warning -Message "Add-InfoBloxDNSRecord - Type $Type not supported" return } $invokeInfobloxQuerySplat = @{ RelativeUri = "record:$Type" Method = 'POST' Body = $Body } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat #-WarningAction SilentlyContinue -WarningVariable varWarning if ($Output) { Write-Verbose -Message "Add-InfoBloxDNSRecord - Added $Type / $Output" } #else { # if (-not $WhatIfPreference) { #Write-Warning -Message "Add-InfoBloxDNSRecord - Failed to add $Type, error: $varWarning" # } #} } function Add-InfobloxFixedAddress { <# .SYNOPSIS Add a fixed mac address to an IP address on an Infoblox server .DESCRIPTION Add a fixed mac address to an IP address on an Infoblox server A fixed address is a specific IP address that a DHCP server always assigns when a lease request comes from a particular MAC address of the client. For example, if you have a printer in your network, you can reserve a particular IP address to be assigned to it every time it is turned on. .PARAMETER IPv4Address IPv4 address to add the mac address to .PARAMETER MacAddress Mac address to add to the IPv4 address .PARAMETER Name Name of the fixed address .PARAMETER Comment Comment for the fixed address .EXAMPLE Add-InfobloxFixedAddress -IPv4Address '10.2.2.18' -MacAddress '00:50:56:9A:00:01' .NOTES General notes #> [cmdletbinding(SupportsShouldProcess)] param( [ValidateNotNullOrEmpty()][parameter(Mandatory)][string] $IPv4Address, [ValidatePattern("([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$")][parameter(Mandatory)][string] $MacAddress, [string] $Name, [string] $Comment, [alias('ms_server')][string] $MicrosoftServer ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Add-InfobloxFixedAddress - You must first connect to an Infoblox server using Connect-Infoblox' return } Write-Verbose -Message "Add-InfobloxFixedAddress - Adding IPv4Address $IPv4Address to MacAddress $MacAddress" $invokeInfobloxQuerySplat = @{ RelativeUri = 'fixedaddress' Method = 'POST' QueryParameter = @{ ipv4addr = $IPv4Address mac = $MacAddress.ToLower() } } if ($Name) { $invokeInfobloxQuerySplat.QueryParameter.name = $Name } if ($Comment) { $invokeInfobloxQuerySplat.QueryParameter.comment = $Comment } if ($MicrosoftServer) { $invokeInfobloxQuerySplat.QueryParameter.ms_server = $MicrosoftServer } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat #-WarningAction SilentlyContinue -WarningVariable varWarning if ($Output) { Write-Verbose -Message "Add-InfobloxFixedAddress - Added $($Mac.ipv4addr) with mac address $($Mac.mac) / $Output" } } function Add-InfobloxNetwork { <# .SYNOPSIS Adds a network to Infoblox. .DESCRIPTION This function adds a network to Infoblox. It requires a connection to an Infoblox server, which can be established using the Connect-Infoblox function. .PARAMETER Network The network to add. This parameter is mandatory. .PARAMETER Comment An optional comment for the network. .PARAMETER NetworkView The network view in which to add the network. Defaults to 'default'. .PARAMETER AutoCreateReverseZone A switch that, when present, indicates that a reverse zone should be automatically created for the network. .PARAMETER DHCPGateway The DHCP gateway for the network. .PARAMETER DHCPLeaseTime The DHCP lease time for the network. .PARAMETER DHCPDomainNameServers The DHCP domain name servers for the network. .PARAMETER Options An array of options to be added to the DHCP range. .PARAMETER ExtensibleAttributeName The name of an extensible attribute for the network. .PARAMETER ExtensibleAttributeSite The site associated with the network as an extensible attribute. .PARAMETER ExtensibleAttributeState The state associated with the network as an extensible attribute. .PARAMETER ExtensibleAttributeCountry The country associated with the network as an extensible attribute. .PARAMETER ExtensibleAttributeRegion The region associated with the network as an extensible attribute. .PARAMETER ExtensibleAttributeVLAN The VLAN associated with the network as an extensible attribute. .PARAMETER ExtensibleAttribute A hashtable of additional extensible attributes to associate with the network. .PARAMETER Members An array of DHCP members to associate with the network. .PARAMETER ReturnOutput A switch that, when present, indicates that the output of the command should be returned. .EXAMPLE Add-InfobloxNetwork -Network '192.168.1.0/24' -Comment 'Test network' -DHCPGateway '192.168.1.1' .EXAMPLE $addInfobloxSubnetSplat = @{ Subnet = '10.22.35.0/24' Comment = "Oki dokii" AutoCreateReverseZone = $true DHCPGateway = "10.22.35.1" DHCPLeaseTime = 5000 DHCPDomainNameServers = "192.168.4.56,192.168.4.57" ExtensinbleAttributeCountry = "Poland" ExtensinbleAttributeName = "Test" ExtensinbleAttributeRegion = "Europe" ExtensinbleAttributeSite = "Site1" ExtensinbleAttributeState = "Mazowieckie" ExtensinbleAttributeVLAN = "810" } Add-InfobloxNetwork @addInfobloxSubnetSplat .EXAMPLE $addInfobloxSubnetSplat = @{ Subnet = '10.22.36.0/24' Comment = "Oki dokii" AutoCreateReverseZone = $true DHCPGateway = "10.22.36.1" DHCPLeaseTime = 5000 DHCPDomainNameServers = "192.168.4.56,192.168.4.57" ExtensinbleAttribute = [ordered] @{ Name = 'Test' VLAN = '810' Country = 'Poland' Region = 'Europe' Site = 'Site1' } } Add-InfobloxNetwork @addInfobloxSubnetSplat .NOTES This function requires a connection to an Infoblox server, which can be established using the Connect-Infoblox function. #> [alias('Add-InfobloxSubnet')] [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)][alias('Subnet')][string] $Network, [string] $Comment, [string] $NetworkView = 'default', [switch] $AutoCreateReverseZone, [string] $DHCPGateway, [string] $DHCPLeaseTime, [string] $DHCPDomainNameServers, [Array] $Options, [string[]] $Members, [string] $ExtensinbleAttributeName, [string] $ExtensinbleAttributeSite, [string] $ExtensinbleAttributeState, [string] $ExtensinbleAttributeCountry, [string] $ExtensinbleAttributeRegion, [string] $ExtensinbleAttributeVLAN, [System.Collections.IDictionary] $ExtensinbleAttribute, [switch] $ReturnOutput ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Add-InfobloxNetwork - You must first connect to an Infoblox server using Connect-Infoblox' return } $Body = [ordered] @{ "network" = $Network # "members" = @( # @{ # "_struct" = "msdhcpserver" # "ipv4addr" = "" # } # ) "comment" = $Comment "network_view" = $NetworkView "auto_create_reversezone" = $AutoCreateReverseZone.IsPresent } Remove-EmptyValue -Hashtable $Body if ($Members) { $Body["members"] = @( foreach ($DHCPMember in $Members) { [ordered] @{ "_struct" = "msdhcpserver" "ipv4addr" = $DHCPMember } } ) #"_struct": "dhcpmember", #"name": "ddi.example.com" } $DHCPOptions = @( if ($Options) { foreach ($Option in $Options) { $Option } } if ($DHCPLeaseTime) { [ordered] @{ "name" = "dhcp-lease-time" "num" = 51 "use_option" = $true "value" = $DHCPLeaseTime "vendor_class" = "DHCP" } } # DNS servers (we will use default ones if not provided) if ($DHCPDomainNameServers) { @{ "name" = "domain-name-servers" "num" = 6 "use_option" = $true "value" = $DHCPDomainNameServers "vendor_class" = "DHCP" } } # DHCP servers (we will use default ones if not provided) if ($DHCPGateway) { @{ "name" = "routers" "num" = 3 "use_option" = $true "value" = $DHCPGateway "vendor_class" = "DHCP" } } ) if ($DHCPOptions.Count -gt 0) { $Body["options"] = $DHCPOptions } # Lets add extensible attributes $ExtensibleAttributeExists = $false foreach ($Key in $PSBoundParameters.Keys) { if ($Key -like "ExtensinbleAttribute*") { $ExtensibleAttributeExists = $true break } } if ($ExtensibleAttributeExists) { $Body["extattrs"] = [ordered] @{} if ($ExtensinbleAttributeName) { $Body["extattrs"]["Name"] = @{ value = $ExtensinbleAttributeName } } if ($ExtensinbleAttributeSite) { $Body["extattrs"]["Site"] = @{ value = $ExtensinbleAttributeSite } } if ($ExtensinbleAttributeState) { $Body["extattrs"]["State"] = @{ value = $ExtensinbleAttributeState } } if ($ExtensinbleAttributeCountry) { $Body["extattrs"]["Country"] = @{ value = $ExtensinbleAttributeCountry } } if ($ExtensinbleAttributeRegion) { $Body["extattrs"]["Region"] = @{ value = $ExtensinbleAttributeRegion } } if ($ExtensinbleAttributeVLAN) { $Body["extattrs"]["VLAN"] = @{ value = $ExtensinbleAttributeVLAN } } foreach ($Key in $ExtensinbleAttribute.Keys) { if ($ExtensinbleAttribute[$Key] -is [System.Collections.IDictionary]) { $Body["extattrs"][$Key] = $ExtensinbleAttribute[$Key] } else { $Body["extattrs"][$Key] = @{ value = $ExtensinbleAttribute[$Key] } } } } $invokeInfobloxQuerySplat = @{ RelativeUri = "network" Method = 'POST' Body = $Body } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat if ($Output) { Write-Verbose -Message "Add-InfobloxNetwork - $Output" if ($ReturnOutput) { $Output } } } function Add-InfobloxNetworkExtensibleAttribute { <# .SYNOPSIS Adds an extensible attribute to a specified network in Infoblox. .DESCRIPTION This function adds an extensible attribute to a network in Infoblox. It requires an established connection to an Infoblox server, which can be done using the Connect-Infoblox function. The function checks if the specified network exists before attempting to add the extensible attribute. .PARAMETER Network The network to which the extensible attribute will be added. This parameter is mandatory. .PARAMETER Attribute The name of the extensible attribute to add. This parameter is mandatory. .PARAMETER Value The value of the extensible attribute to add. This parameter is mandatory. .EXAMPLE Add-InfobloxNetworkExtensibleAttribute -Network '192.168.1.0/24' -Attribute 'Location' -Value 'Data Center 1' Adds the 'Location' extensible attribute with the value 'Data Center 1' to the network '192.168.1.0/24'. .NOTES You must first connect to an Infoblox server using Connect-Infoblox before running this function. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)][alias('Subnet')][string] $Network, [Parameter(Mandatory)][alias('ExtensinbleAttribute')][string] $Attribute, [Parameter(Mandatory)][alias('ExtensinbleAttributeValue')][string] $Value ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Add-InfobloxNetworkExtensibleAttribute - You must first connect to an Infoblox server using Connect-Infoblox' return } $NetworkInformation = Get-InfobloxNetwork -Network $Network if (-not $NetworkInformation._ref) { Write-Warning -Message "Add-InfobloxNetworkExtensibleAttribute - Network $Network not found" return } $Body = [ordered] @{ "extattrs+" = @{ $Attribute = @{ "value" = $Value } } } Remove-EmptyValue -Hashtable $Body $invokeInfobloxQuerySplat = @{ RelativeUri = $NetworkInformation._ref Method = 'PUT' Body = $Body } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat if ($Output) { Write-Verbose -Message "Add-InfobloxNetworkExtensibleAttribute - $Output" if ($ReturnOutput) { $Output } } } function Connect-Infoblox { [CmdletBinding()] param( [Parameter(Mandatory, ParameterSetName = 'UserName')] [Parameter(Mandatory, ParameterSetName = 'Credential')] [string] $Server, [Parameter(Mandatory, ParameterSetName = 'UserName')][string] $Username, [alias('SecurePassword')][Parameter(Mandatory, ParameterSetName = 'UserName')][string] $EncryptedPassword, [Parameter(Mandatory, ParameterSetName = 'Credential')][pscredential] $Credential, [Parameter(ParameterSetName = 'UserName')] [Parameter(ParameterSetName = 'Credential')] [string] $ApiVersion = '2.11', [Parameter(ParameterSetName = 'UserName')] [Parameter(ParameterSetName = 'Credential')] [switch] $EnableTLS12, [Parameter(ParameterSetName = 'UserName')] [Parameter(ParameterSetName = 'Credential')] [switch] $AllowSelfSignedCerts, [Parameter(ParameterSetName = 'UserName')] [Parameter(ParameterSetName = 'Credential')] [switch] $SkipInitialConnection, [switch] $ReturnObject ) if ($EnableTLS12) { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } # lets clear sessions if any exists Disconnect-Infoblox if ($Username -and $EncryptedPassword) { try { $Password = $EncryptedPassword | ConvertTo-SecureString -ErrorAction Stop $Credential = [pscredential]::new($Username, $Password) } catch { if ($ErrorActionPreference -eq 'Stop') { throw } Write-Warning -Message "Connect-Infoblox - Unable to convert password to secure string. Error: $($_.Exception.Message)" return } } $PSDefaultParameterValues['Invoke-InfobloxQuery:Credential'] = $Credential $PSDefaultParameterValues['Invoke-InfobloxQuery:Server'] = $Server $PSDefaultParameterValues['Invoke-InfobloxQuery:BaseUri'] = "https://$Server/wapi/v$apiVersion" $PSDefaultParameterValues['Invoke-InfobloxQuery:WebSession'] = [Microsoft.PowerShell.Commands.WebRequestSession]::new() # $WebSession = New-WebSession -Cookies @{ # security_setting = @{ # session_timeout = 60000 # } # } -For $BaseUri # The infoblox configuration is not really used anywhere. It's just a placeholder $Script:InfobloxConfiguration = [ordered] @{ ApiVersion = $ApiVersion #Credential = $Credential Server = $Server BaseUri = "https://$Server/wapi/v$apiVersion" # Create a WebSession object to store cookies # Session = New-Object Microsoft.PowerShell.Commands.WebRequestSession #WebSession = $WebSession } if ($AllowSelfSignedCerts) { Hide-SelfSignedCerts } # we do inital query to make sure we're connected if (-not $SkipInitialConnection) { $Schema = Get-InfobloxSchema -WarningAction SilentlyContinue if (-not $Schema) { Disconnect-Infoblox return } } if ($ReturnObject) { $Script:InfobloxConfiguration } } function Disconnect-Infoblox { <# .SYNOPSIS Disconnects from an InfoBlox server .DESCRIPTION Disconnects from an InfoBlox server As this is a REST API it doesn't really disconnect, but it does clear the script variable to clear the credentials from memory .EXAMPLE Disconnect-Infoblox .NOTES General notes #> [cmdletbinding(SupportsShouldProcess)] param( [switch] $ForceLogOut ) if ($ForceLogOut) { $invokeInfobloxQuerySplat = @{ RelativeUri = "logout" Method = 'POST' } Invoke-InfobloxQuery @invokeInfobloxQuerySplat } # lets remove the default parameters so that user has to connect again $Script:InfobloxConfiguration = $null $PSDefaultParameterValues.Remove('Invoke-InfobloxQuery:Credential') $PSDefaultParameterValues.Remove('Invoke-InfobloxQuery:Server') $PSDefaultParameterValues.Remove('Invoke-InfobloxQuery:BaseUri') $PSDefaultParameterValues.Remove('Invoke-InfobloxQuery:WebSession') } function Get-InfobloxDHCPLease { [alias('Get-InfobloxDHCPLeases')] [CmdletBinding()] param( [string] $Network, [string] $IPv4Address, [string] $Hostname, [switch] $PartialMatch ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDHCPLease - You must first connect to an Infoblox server using Connect-Infoblox' return } Write-Verbose -Message "Get-InfobloxDHCPLease - Requesting DHCP leases for Network [$Network] / IPv4Address [$IPv4Address] / Hostname [$Hostname] / PartialMatch [$($PartialMatch.IsPresent)]" $invokeInfobloxQuerySplat = @{ RelativeUri = 'lease' Method = 'GET' QueryParameter = @{ _return_fields = 'binding_state,hardware,client_hostname,fingerprint,address,network_view' _max_results = 1000000 } } if ($Network) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."network~" = $Network.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.network = $Network.ToLower() } } if ($IPv4Address) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."ipv4addr~" = $IPv4Address.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.ipv4addr = $IPv4Address.ToLower() } } if ($Hostname) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."name~" = $Hostname.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.name = $Hostname.ToLower() } } Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false } function Get-InfobloxDHCPRange { [CmdletBinding()] param( [string] $Network, [switch] $PartialMatch, [switch] $FetchFromSchema, [int] $MaxResults = 1000000 ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDHCPRange - You must first connect to an Infoblox server using Connect-Infoblox' return } if ($Network) { Write-Verbose -Message "Get-InfobloxDHCPRange - Requesting DHCP ranges for Network [$Network] / PartialMatch [$($PartialMatch.IsPresent)]" } else { Write-Verbose -Message "Get-InfobloxDHCPRange - Requesting DHCP ranges for all networks" } if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "range" } else { $ReturnFields = $Null } $invokeInfobloxQuerySplat = @{ RelativeUri = 'range' Method = 'GET' QueryParameter = @{ _max_results = $MaxResults _return_fields = $ReturnFields } } if ($Network) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."network~" = $Network.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.network = $Network.ToLower() } } Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false } function Get-InfobloxDiscoveryTask { [cmdletbinding()] param( [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDiscoveryTask - You must first connect to an Infoblox server using Connect-Infoblox' return } # defalt return fields if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "discoverytask" } $invokeInfobloxQuerySplat = @{ RelativeUri = 'discoverytask' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = $ReturnFields } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -LastProperty '_ref' } function Get-InfobloxDNSAuthZone { [alias('Get-InfobloxDNSAuthZones')] [cmdletbinding()] param( [string] $FQDN, [string] $View ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDNSAuthZones - You must first connect to an Infoblox server using Connect-Infoblox' return } $invokeInfobloxQuerySplat = @{ RelativeUri = 'zone_auth' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = @( 'address' 'allow_active_dir' 'allow_fixed_rrset_order' 'allow_gss_tsig_for_underscore_zone' 'allow_gss_tsig_zone_updates' 'allow_query' 'allow_transfer' 'allow_update' 'allow_update_forwarding' 'aws_rte53_zone_info' 'cloud_info' 'comment' 'copy_xfer_to_notify' 'create_underscore_zones' 'ddns_force_creation_timestamp_update' 'ddns_principal_group' 'ddns_principal_tracking' 'ddns_restrict_patterns' 'ddns_restrict_patterns_list' 'ddns_restrict_protected' 'ddns_restrict_secure' 'ddns_restrict_static' 'disable' 'disable_forwarding' 'display_domain' 'dns_fqdn' 'dns_integrity_enable' 'dns_integrity_frequency' 'dns_integrity_member' 'dns_integrity_verbose_logging' 'dns_soa_email' 'dnssec_key_params' 'dnssec_keys' 'dnssec_ksk_rollover_date' 'dnssec_zsk_rollover_date' 'effective_check_names_policy' 'effective_record_name_policy' 'extattrs' 'external_primaries' 'external_secondaries' 'fqdn' 'grid_primary' 'grid_primary_shared_with_ms_parent_delegation' 'grid_secondaries' 'is_dnssec_enabled' 'is_dnssec_signed' 'is_multimaster' 'last_queried' 'locked' 'locked_by' 'mask_prefix' 'member_soa_mnames' 'member_soa_serials' 'ms_ad_integrated' 'ms_allow_transfer' 'ms_allow_transfer_mode' 'ms_dc_ns_record_creation' 'ms_ddns_mode' 'ms_managed' 'ms_primaries' 'ms_read_only' 'ms_secondaries' 'ms_sync_disabled' 'ms_sync_master_name' 'network_associations' 'network_view' 'notify_delay' 'ns_group' 'parent' 'prefix' 'primary_type' 'record_name_policy' 'records_monitored' 'rr_not_queried_enabled_time' 'scavenging_settings' 'soa_default_ttl' 'soa_email' 'soa_expire' 'soa_negative_ttl' 'soa_refresh' 'soa_retry' 'soa_serial_number' 'srgs' 'update_forwarding' 'use_allow_active_dir' 'use_allow_query' 'use_allow_transfer' 'use_allow_update' 'use_allow_update_forwarding' 'use_check_names_policy' 'use_copy_xfer_to_notify' 'use_ddns_force_creation_timestamp_update' 'use_ddns_patterns_restriction' 'use_ddns_principal_security' 'use_ddns_restrict_protected' 'use_ddns_restrict_static' 'use_dnssec_key_params' 'use_external_primary' 'use_grid_zone_timer' 'use_import_from' 'use_notify_delay' 'use_record_name_policy' 'use_scavenging_settings' 'use_soa_email' 'using_srg_associations' 'view' 'zone_format' 'zone_not_queried_enabled_time' ) -join ',' } } if ($View) { $invokeInfobloxQuerySplat.QueryParameter.view = $View.ToLower() } if ($FQDN) { $invokeInfobloxQuerySplat.QueryParameter.fqdn = $FQDN.ToLower() } Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false | Select-ObjectByProperty -LastProperty '_ref' } function Get-InfobloxDNSDelegatedZone { [cmdletbinding()] param( [string] $Name, [string] $View ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDNSDelgatedZone - You must first connect to an Infoblox server using Connect-Infoblox' return } $invokeInfobloxQuerySplat = @{ RelativeUri = 'zone_delegated' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = "address,comment,delegate_to,delegated_ttl,disable,display_domain,dns_fqdn,enable_rfc2317_exclusion,extattrs,fqdn,locked,locked_by,mask_prefix,ms_ad_integrated,ms_ddns_mode,ms_managed,ms_read_only,ms_sync_master_name,ns_group,parent,prefix,use_delegated_ttl,using_srg_associations,view,zone_format" } } if ($View) { $invokeInfobloxQuerySplat.QueryParameter.view = $View.ToLower() } if ($Name) { $invokeInfobloxQuerySplat.QueryParameter.fqdn = $Name.ToLower() } Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false | Select-ObjectByProperty -LastProperty '_ref' } function Get-InfobloxDNSForwardZone { [cmdletbinding()] param( [string] $Name, [string] $View ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDNSForwardZone - You must first connect to an Infoblox server using Connect-Infoblox' return } $invokeInfobloxQuerySplat = @{ RelativeUri = 'zone_forward' Method = 'GET' QueryParameter = @{ _max_results = 1000000 } } if ($View) { $invokeInfobloxQuerySplat.QueryParameter.view = $View.ToLower() } if ($Name) { $invokeInfobloxQuerySplat.QueryParameter.fqdn = $Name.ToLower() } Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false | Select-ObjectByProperty -LastProperty '_ref' } function Get-InfobloxDNSRecord { [alias('Get-InfobloxDNSRecords')] [cmdletbinding()] param( [string] $Name, [string] $Zone, [string] $View, [switch] $PartialMatch, [ValidateSet( 'A', 'AAAA', 'CName', 'DName', 'DNSKEY', 'DS', 'Host', 'host_ipv4addr', 'host_ipv6addr', 'LBDN', 'MX', 'NAPTR', 'NS', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'PTR', 'RRSIG', 'SRV', 'TXT' )] [string] $Type = 'Host', [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDNSRecord - You must first connect to an Infoblox server using Connect-Infoblox' return } $invokeInfobloxQuerySplat = @{ RelativeUri = "record:$($Type.ToLower())" Method = 'GET' QueryParameter = @{ _max_results = 1000000 } } if ($Type -eq 'Host') { $invokeInfobloxQuerySplat.QueryParameter._return_fields = 'name,dns_name,aliases,dns_aliases,ipv4addrs,configure_for_dns,view' } elseif ($Type -eq 'PTR') { $invokeInfobloxQuerySplat.QueryParameter._return_fields = 'aws_rte53_record_info,cloud_info,comment,creation_time,creator,ddns_principal,ddns_protected,disable,discovered_data,dns_name,dns_ptrdname,extattrs,forbid_reclamation,ipv4addr,ipv6addr,last_queried,ms_ad_user_data,name,ptrdname,reclaimable,shared_record_group,ttl,use_ttl,view,zone' } elseif ($Type -eq 'A') { $invokeInfobloxQuerySplat.QueryParameter._return_fields = 'ipv4addr,name,view,zone,cloud_info,comment,creation_time,creator,ddns_principal,ddns_protected,disable,discovered_data,dns_name,last_queried,ms_ad_user_data,reclaimable,shared_record_group,ttl,use_ttl' } if ($FetchFromSchema) { $invokeInfobloxQuerySplat.QueryParameter._return_fields = Get-FieldsFromSchema -SchemaObject "record:$Type" } if ($Zone) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."zone~" = $Zone.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.zone = $Zone.ToLower() } } if ($View) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."view~" = $View.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.view = $View.ToLower() } } if ($Name) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."name~" = $Name.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.name = $Name.ToLower() } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false if ($Type -eq 'A') { $Output | Select-ObjectByProperty -LastProperty '_ref' -FirstProperty 'name', 'ipv4addr', 'view', 'zone', 'cloud_info', 'comment', 'creation_time', 'creator', 'ddns_principal', 'ddns_protected', 'disable', 'discovered_data', 'dns_name', 'last_queried', 'ms_ad_user_data', 'reclaimable', 'shared_record_group', 'ttl', 'use_ttl' } elseif ($Type -eq 'HOST') { $Output | Select-ObjectByProperty -LastProperty '_ref' -FirstProperty 'name', 'dns_name', 'aliases', 'dns_aliases', 'view', 'configure_for_dns', 'configure_for_dhcp', 'host', 'ipv4addr', 'ipv4addr_ref' } else { $Output | Select-ObjectByProperty -LastProperty '_ref' } } function Get-InfobloxDNSRecordAll { [alias('Get-InfobloxDNSRecordsAll')] [cmdletbinding()] param( [string] $Name, [string] $Zone, [string] $View, [switch] $PartialMatch, [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDNSRecordAll - You must first connect to an Infoblox server using Connect-Infoblox' return } $invokeInfobloxQuerySplat = @{ RelativeUri = "allrecords" Method = 'GET' QueryParameter = @{ _max_results = 1000000 } } $invokeInfobloxQuerySplat.QueryParameter._return_fields = 'address,comment,creator,ddns_principal,ddns_protected,disable,dtc_obscured,name,reclaimable,record,ttl,type,view,zone' if ($FetchFromSchema) { <# if (-not $Script:InfobloxSchemaFields) { $Script:InfobloxSchemaFields = [ordered] @{} } if ($Script:InfobloxSchemaFields["allrecords"]) { $invokeInfobloxQuerySplat.QueryParameter._return_fields = ($Script:InfobloxSchemaFields["allrecords"]) } else { $Schema = Get-InfobloxSchema -Object "allrecords" if ($Schema -and $Schema.fields.name) { $invokeInfobloxQuerySplat.QueryParameter._return_fields = ($Schema.fields.Name -join ',') $Script:InfobloxSchemaFields["allrecords"] = ($Schema.fields.Name -join ',') } else { Write-Warning -Message "Get-InfobloxDNSRecordAll - Failed to fetch schema for record type 'allrecords'. Using defaults" } } #> $invokeInfobloxQuerySplat.QueryParameter._return_fields = Get-FieldsFromSchema -SchemaObject "allrecords" } if ($Zone) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."zone~" = $Zone.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.zone = $Zone.ToLower() } } if ($View) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."view~" = $View.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.view = $View.ToLower() } } if ($Name) { if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."name~" = $Name.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.name = $Name.ToLower() } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $AllProperties = Select-Properties -AllProperties -Object $Output $Output | Select-ObjectByProperty -LastProperty '_ref' -FirstProperty 'zone', 'type', 'name', 'address', 'disable', 'creator' -AllProperties $AllProperties } function Get-InfobloxDNSView { [cmdletbinding()] param( ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxDNSView - You must first connect to an Infoblox server using Connect-Infoblox' return } Write-Verbose -Message "Get-InfobloxDNSView - Requesting DNS View" $invokeInfobloxQuerySplat = @{ RelativeUri = 'view' Method = 'GET' QueryParameter = @{ _max_results = 1000000 # _return_fields = 'mac,ipv4addr,network_view' } } Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false } function Get-InfobloxFixedAddress { [cmdletbinding()] param( [parameter(Mandatory)][string] $MacAddress, [switch] $PartialMatch, [switch] $FetchFromSchema, [string[]] $Properties ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxFixedAddress - You must first connect to an Infoblox server using Connect-Infoblox' return } Write-Verbose -Message "Get-InfobloxFixedAddress - Requesting MacAddress [$MacAddress] / PartialMatch [$($PartialMatch.IsPresent)]" $invokeInfobloxQuerySplat = @{ RelativeUri = 'fixedaddress' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = 'mac,ipv4addr,network_view' } } if ($FetchFromSchema) { $invokeInfobloxQuerySplat.QueryParameter._return_fields = Get-FieldsFromSchema -SchemaObject "fixedaddress" } elseif ($Properties) { $invokeInfobloxQuerySplat.QueryParameter._return_fields = $Properties -join ',' } if ($PartialMatch) { $invokeInfobloxQuerySplat.QueryParameter."mac~" = $MacAddress.ToLower() } else { $invokeInfobloxQuerySplat.QueryParameter.mac = $MacAddress.ToLower() } Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false } function Get-InfobloxGrid { [cmdletbinding()] param( [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxGrid - You must first connect to an Infoblox server using Connect-Infoblox' return } # defalt return fields $ReturnFields = 'allow_recursive_deletion,audit_log_format,audit_to_syslog_enable,automated_traffic_capture_setting,consent_banner_setting,csp_api_config,csp_grid_setting,deny_mgm_snapshots,descendants_action,dns_resolver_setting,dscp,email_setting,enable_gui_api_for_lan_vip,enable_lom,enable_member_redirect,enable_recycle_bin,enable_rir_swip,external_syslog_backup_servers,external_syslog_server_enable,http_proxy_server_setting,informational_banner_setting,is_grid_visualization_visible,lockout_setting,lom_users,mgm_strict_delegate_mode,ms_setting,name,nat_groups,ntp_setting,objects_changes_tracking_setting,password_setting,restart_banner_setting,restart_status,rpz_hit_rate_interval,rpz_hit_rate_max_query,rpz_hit_rate_min_query,scheduled_backup,security_banner_setting,security_setting,service_status,snmp_setting,support_bundle_download_timeout,syslog_facility,syslog_servers,syslog_size,threshold_traps,time_zone,token_usage_delay,traffic_capture_auth_dns_setting,traffic_capture_chr_setting,traffic_capture_qps_setting,traffic_capture_rec_dns_setting,traffic_capture_rec_queries_setting,trap_notifications,updates_download_member_config,vpn_port' if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "grid" } $invokeInfobloxQuerySplat = @{ RelativeUri = 'grid' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = $ReturnFields } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -LastProperty '_ref' } function Get-InfobloxIPAddress { <# .SYNOPSIS Get Infoblox IP Address information for given network or IP address .DESCRIPTION Get Infoblox IP Address information for given network or IP address .PARAMETER Network Find IP address information for a specific network .PARAMETER IPv4Address Find IP address information for a specific IP address .PARAMETER Status Get IP addresses with a specific status, either Used or Unused .PARAMETER Name Get IP addresses with a specific name .PARAMETER Count Limit the number of results returned .EXAMPLE Get-InfobloxIPAddress -Network '10.2.2.0/24' .EXAMPLE Get-InfobloxIPAddress -Network '10.2.2.0/24' -Status Used -Verbose | Format-Table .EXAMPLE Get-InfobloxIPAddress -Network '10.2.2.0' -Verbose | Format-Table .NOTES General notes #> [cmdletbinding()] param( [parameter(ParameterSetName = 'Network')][string] $Network, [parameter(ParameterSetName = 'IPv4')][string] $IPv4Address, [parameter(ParameterSetName = 'Network')] [parameter(ParameterSetName = 'IPv4')] [parameter()][ValidateSet('Used', 'Unused')][string] $Status, [parameter(ParameterSetName = 'Network')] [parameter(ParameterSetName = 'IPv4')] [parameter()][string] $Name, [parameter(ParameterSetName = 'Network')] [parameter(ParameterSetName = 'IPv4')] [alias('Quantity')][parameter()][int] $Count ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxIPAddress - You must first connect to an Infoblox server using Connect-Infoblox' return } if ($Network) { Write-Verbose -Message "Get-InfobloxIPAddress - Requesting Network [$Network] Status [$Status]" } else { Write-Verbose -Message "Get-InfobloxIPAddress - Requesting IPv4Address [$IPv4Address] Status [$Status]" } $invokeInfobloxQuerySplat = [ordered]@{ RelativeUri = 'ipv4address' Method = 'GET' QueryParameter = [ordered]@{ _max_results = 1000000 } } if ($Network) { $invokeInfobloxQuerySplat.QueryParameter.network = $Network } if ($Status) { $invokeInfobloxQuerySplat.QueryParameter.status = $Status.ToUpper() } if ($Name) { $invokeInfobloxQuerySplat.QueryParameter.names = $Name } if ($IPv4Address) { $invokeInfobloxQuerySplat.QueryParameter.ip_address = $IPv4Address } if ($Count) { Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false | Select-Object -First $Count | Select-ObjectByProperty -LastProperty '_ref' } else { Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false | Select-ObjectByProperty -LastProperty '_ref' } } function Get-InfobloxMember { [cmdletbinding()] param( [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxMember - You must first connect to an Infoblox server using Connect-Infoblox' return } # defalt return fields $ReturnFields = 'config_addr_type,host_name,platform,service_type_configuration,vip_setting,node_info,service_status' if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "member" } $invokeInfobloxQuerySplat = @{ RelativeUri = 'member' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = $ReturnFields } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -FirstProperty 'host_name' -LastProperty '_ref' } function Get-InfobloxNetwork { [OutputType([system.object[]])] [cmdletbinding()] param( [string] $Network, [string[]]$Properties, [switch] $Partial, [switch] $All, [int] $MaxResults = 1000000, [switch] $Native ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxNetwork - You must first connect to an Infoblox server using Connect-Infoblox' return } $QueryParameter = [ordered]@{ _return_fields = @( "authority", "bootfile", "bootserver", "cloud_info", "comment", "conflict_count", "ddns_domainname", "ddns_generate_hostname", "ddns_server_always_updates", "ddns_ttl", "ddns_update_fixed_addresses", "ddns_use_option81", "deny_bootp", "dhcp_utilization", "dhcp_utilization_status", "disable", "discover_now_status", "discovered_bgp_as", "discovered_bridge_domain", "discovered_tenant", "discovered_vlan_id", "discovered_vlan_name", "discovered_vrf_description", "discovered_vrf_name", "discovered_vrf_rd", "discovery_basic_poll_settings", "discovery_blackout_setting", "discovery_engine_type", "discovery_member", "dynamic_hosts", "email_list", "enable_ddns", "enable_dhcp_thresholds", "enable_discovery", "enable_email_warnings", "enable_ifmap_publishing", "enable_pxe_lease_time", "enable_snmp_warnings", "endpoint_sources", "extattrs", "high_water_mark", "high_water_mark_reset", "ignore_dhcp_option_list_request", "ignore_id", "ignore_mac_addresses", "ipam_email_addresses", "ipam_threshold_settings", "ipam_trap_settings", "ipv4addr", "last_rir_registration_update_sent", "last_rir_registration_update_status", "lease_scavenge_time", "logic_filter_rules", "low_water_mark", "low_water_mark_reset", "members", "mgm_private", "mgm_private_overridable", "ms_ad_user_data", "netmask", "network", "network_container", "network_view", "nextserver", "options", "port_control_blackout_setting", "pxe_lease_time", "recycle_leases", "rir", "rir_organization", "rir_registration_status", "same_port_control_discovery_blackout", "static_hosts", "subscribe_settings", "total_hosts", "unmanaged", "unmanaged_count", "update_dns_on_lease_renewal", "use_authority", "use_blackout_setting", "use_bootfile", "use_bootserver", "use_ddns_domainname", "use_ddns_generate_hostname", "use_ddns_ttl", "use_ddns_update_fixed_addresses", "use_ddns_use_option81", "use_deny_bootp", "use_discovery_basic_polling_settings", "use_email_list", "use_enable_ddns", "use_enable_dhcp_thresholds", "use_enable_discovery", "use_enable_ifmap_publishing", "use_ignore_dhcp_option_list_request", "use_ignore_id", "use_ipam_email_addresses", "use_ipam_threshold_settings", "use_ipam_trap_settings", "use_lease_scavenge_time", "use_logic_filter_rules", "use_mgm_private", "use_nextserver", "use_options", "use_pxe_lease_time", "use_recycle_leases", "use_subscribe_settings", "use_update_dns_on_lease_renewal", "use_zone_associations", "utilization", "utilization_update", "vlans", "zone_associations" ) -join ',' } if ($All) { $QueryParameter["_max_results"] = $MaxResults } elseif ($Network -and $Partial.IsPresent) { $QueryParameter["_max_results"] = $MaxResults $QueryParameter."network~" = $Network } elseif ($Network) { $QueryParameter.network = $Network } else { Write-Warning -Message "Get-InfobloxNetwork - You must provide either -Network or -All switch" return } $ListNetworks = Invoke-InfobloxQuery -RelativeUri "network" -Method Get -QueryParameter $QueryParameter -WhatIf:$false foreach ($FoundNetwork in $ListNetworks) { if ($Native) { $FoundNetwork } else { $FullInformation = Get-IPAddressRangeInformation -Network $FoundNetwork.network $OutputData = [ordered] @{ Network = $FoundNetwork.network NetworkRef = $FoundNetwork._ref IP = $FullInformation.IP # : 10.2.10.0 NetworkLength = $FullInformation.NetworkLength # : 24 SubnetMask = $FullInformation.SubnetMask # : 255.255.255.0 NetworkAddress = $FullInformation.NetworkAddress # : 10.2.10.0 HostMin = $FullInformation.HostMin # : 10.2.10.1 HostMax = $FullInformation.HostMax # : 10.2.10.254 TotalHosts = $FullInformation.TotalHosts # : 256 UsableHosts = $FullInformation.UsableHosts # : 254 Broadcast = $FullInformation.Broadcast # : 10.2.10.255 authority = $FoundNetwork.authority #: False cloud_info = $FoundNetwork.cloud_info #: @{authority_type=GM; delegated_scope=NONE; mgmt_platform=; owned_by_adaptor=False} comment = $FoundNetwork.comment #: found in CMDB conflict_count = $FoundNetwork.conflict_count #: 0 ddns_generate_hostname = $FoundNetwork.ddns_generate_hostname #: False ddns_server_always_updates = $FoundNetwork.ddns_server_always_updates #: True ddns_ttl = $FoundNetwork.ddns_ttl #: 0 ddns_update_fixed_addresses = $FoundNetwork.ddns_update_fixed_addresses #: False ddns_use_option81 = $FoundNetwork.ddns_use_option81 #: False deny_bootp = $FoundNetwork.deny_bootp #: False dhcp_utilization = $FoundNetwork.dhcp_utilization #: 0 dhcp_utilization_status = $FoundNetwork.dhcp_utilization_status #: LOW disable = $FoundNetwork.disable #: False discover_now_status = $FoundNetwork.discover_now_status #: NONE discovered_bgp_as = $FoundNetwork.discovered_bgp_as #: discovered_bridge_domain = $FoundNetwork.discovered_bridge_domain #: discovered_tenant = $FoundNetwork.discovered_tenant #: discovered_vlan_id = $FoundNetwork.discovered_vlan_id #: discovered_vlan_name = $FoundNetwork.discovered_vlan_name #: discovered_vrf_description = $FoundNetwork.discovered_vrf_description #: discovered_vrf_name = $FoundNetwork.discovered_vrf_name #: discovered_vrf_rd = $FoundNetwork.discovered_vrf_rd #: discovery_basic_poll_settings = $FoundNetwork.discovery_basic_poll_settings #: @{auto_arp_refresh_before_switch_port_polling=True; cli_collection=True; complete_ping_sweep=False; # = $FoundNetwork. # credential_group=default; device_profile=False; netbios_scanning=False; port_scanning=False; # = $FoundNetwork. # smart_subnet_ping_sweep=False; snmp_collection=True; switch_port_data_collection_polling=PERIODIC; # = $FoundNetwork. # switch_port_data_collection_polling_interval=3600} discovery_blackout_setting = $FoundNetwork.discovery_blackout_setting #: @{enable_blackout=False} discovery_engine_type = $FoundNetwork.discovery_engine_type #: NONE dynamic_hosts = $FoundNetwork.dynamic_hosts #: 0 email_list = $FoundNetwork.email_list #: {} enable_ddns = $FoundNetwork.enable_ddns #: False enable_dhcp_thresholds = $FoundNetwork.enable_dhcp_thresholds #: False enable_discovery = $FoundNetwork.enable_discovery #: False enable_email_warnings = $FoundNetwork.enable_email_warnings #: False enable_ifmap_publishing = $FoundNetwork.enable_ifmap_publishing #: False enable_pxe_lease_time = $FoundNetwork.enable_pxe_lease_time #: False enable_snmp_warnings = $FoundNetwork.enable_snmp_warnings #: False #extattrs = $FoundNetwork.extattrs #: @{Country=; Name=; Region=} high_water_mark = $FoundNetwork.high_water_mark #: 95 high_water_mark_reset = $FoundNetwork.high_water_mark_reset #: 85 ignore_dhcp_option_list_request = $FoundNetwork.ignore_dhcp_option_list_request #: False ignore_id = $FoundNetwork.ignore_id #: NONE ignore_mac_addresses = $FoundNetwork.ignore_mac_addresses #: {} ipam_email_addresses = $FoundNetwork.ipam_email_addresses #: {} ipam_threshold_settings = $FoundNetwork.ipam_threshold_settings #: @{reset_value=85; trigger_value=95} ipam_trap_settings = $FoundNetwork.ipam_trap_settings #: @{enable_email_warnings=False; enable_snmp_warnings=True} ipv4addr = $FoundNetwork.ipv4addr #: 172.23.0.0 lease_scavenge_time = $FoundNetwork.lease_scavenge_time #: -1 logic_filter_rules = $FoundNetwork.logic_filter_rules #: {} low_water_mark = $FoundNetwork.low_water_mark #: 0 low_water_mark_reset = $FoundNetwork.low_water_mark_reset #: 10 members = $FoundNetwork.members #: {} mgm_private = $FoundNetwork.mgm_private #: False mgm_private_overridable = $FoundNetwork.mgm_private_overridable #: True netmask = $FoundNetwork.netmask #: 27 network_container = $FoundNetwork.network_container #: 172.23.0.0/16 network_view = $FoundNetwork.network_view #: default options = $FoundNetwork.options #: {@{name=dhcp-lease-time; num=51; use_option=False; value=43200; vendor_class=DHCP}} port_control_blackout_setting = $FoundNetwork.port_control_blackout_setting #: @{enable_blackout=False} recycle_leases = $FoundNetwork.recycle_leases #: True rir = $FoundNetwork.rir #: NONE rir_registration_status = $FoundNetwork.rir_registration_status #: NOT_REGISTERED same_port_control_discovery_blackout = $FoundNetwork.same_port_control_discovery_blackout #: False static_hosts = $FoundNetwork.static_hosts #: 0 subscribe_settings = $FoundNetwork.subscribe_settings #: total_hosts = $FoundNetwork.total_hosts #: 0 unmanaged = $FoundNetwork.unmanaged #: False unmanaged_count = $FoundNetwork.unmanaged_count #: 0 update_dns_on_lease_renewal = $FoundNetwork.update_dns_on_lease_renewal #: False use_authority = $FoundNetwork.use_authority #: False use_blackout_setting = $FoundNetwork.use_blackout_setting #: False use_bootfile = $FoundNetwork.use_bootfile #: False use_bootserver = $FoundNetwork.use_bootserver #: False use_ddns_domainname = $FoundNetwork.use_ddns_domainname #: False use_ddns_generate_hostname = $FoundNetwork.use_ddns_generate_hostname #: False use_ddns_ttl = $FoundNetwork.use_ddns_ttl #: False use_ddns_update_fixed_addresses = $FoundNetwork.use_ddns_update_fixed_addresses #: False use_ddns_use_option81 = $FoundNetwork.use_ddns_use_option81 #: False use_deny_bootp = $FoundNetwork.use_deny_bootp #: False use_discovery_basic_polling_settings = $FoundNetwork.use_discovery_basic_polling_settings #: False use_email_list = $FoundNetwork.use_email_list #: False use_enable_ddns = $FoundNetwork.use_enable_ddns #: False use_enable_dhcp_thresholds = $FoundNetwork.use_enable_dhcp_thresholds #: False use_enable_discovery = $FoundNetwork.use_enable_discovery #: False use_enable_ifmap_publishing = $FoundNetwork.use_enable_ifmap_publishing #: False use_ignore_dhcp_option_list_request = $FoundNetwork.use_ignore_dhcp_option_list_request #: False use_ignore_id = $FoundNetwork.use_ignore_id #: False use_ipam_email_addresses = $FoundNetwork.use_ipam_email_addresses #: False use_ipam_threshold_settings = $FoundNetwork.use_ipam_threshold_settings #: False use_ipam_trap_settings = $FoundNetwork.use_ipam_trap_settings #: False use_lease_scavenge_time = $FoundNetwork.use_lease_scavenge_time #: False use_logic_filter_rules = $FoundNetwork.use_logic_filter_rules #: False use_mgm_private = $FoundNetwork.use_mgm_private #: False use_nextserver = $FoundNetwork.use_nextserver #: False use_options = $FoundNetwork.use_options #: False use_pxe_lease_time = $FoundNetwork.use_pxe_lease_time #: False use_recycle_leases = $FoundNetwork.use_recycle_leases #: False use_subscribe_settings = $FoundNetwork.use_subscribe_settings #: False use_update_dns_on_lease_renewal = $FoundNetwork.use_update_dns_on_lease_renewal #: False use_zone_associations = $FoundNetwork.use_zone_associations #: False utilization = $FoundNetwork.utilization #: 0 utilization_update = $FoundNetwork.utilization_update #: 1707318915 vlans = $FoundNetwork.vlans #: {} zone_associations = $FoundNetwork.zone_associations #: {} _ref = $FoundNetwork._ref #: network/ZG } foreach ($Extra in $FoundNetwork.extattrs.psobject.properties) { $OutputData[$Extra.Name] = $Extra.Value.value } [PSCustomObject]$OutputData } } } function Get-InfobloxNetworkNextAvailableIP { [cmdletbinding(DefaultParameterSetName = 'Network')] param( [Parameter(Mandatory, ParameterSetName = 'Network')][string] $Network, [Parameter(Mandatory, ParameterSetName = 'NetworkRef')][string] $NetworkRef, [alias('Count')][int] $Quantity = 1 ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxNetworkNextAvailableIP - You must first connect to an Infoblox server using Connect-Infoblox' return } if ($Network) { $NetworkInformation = Get-InfobloxNetwork -Network $Network if ($NetworkInformation) { $NetworkRef = $NetworkInformation.NetworkRef } else { Write-Warning -Message "Get-InfobloxNetworkNextAvailableIP - No network found for [$Network]" return } } $invokeInfobloxQuerySplat = @{ RelativeUri = $NetworkRef QueryParameter = @{ _function = 'next_available_ip' num = $Quantity } Method = 'POST' } $Query = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WarningAction SilentlyContinue -WarningVariable varWarning -WhatIf:$false if ($Query) { $Query.ips } else { Write-Warning -Message "Get-InfobloxNetworkNextAvailableIP - No IP returned for network [$NetworkRef], error: $varWarning" } } function Get-InfobloxNetworkView { [cmdletbinding()] param( [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxNetworkView - You must first connect to an Infoblox server using Connect-Infoblox' return } Write-Verbose -Message "Get-InfobloxNetworkView - Requesting Network View" if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "networkview" } $invokeInfobloxQuerySplat = @{ RelativeUri = 'networkview' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = $ReturnFields } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -LastProperty '_ref' } function Get-InfobloxObjects { [CmdletBinding()] param( [Parameter(Mandatory, ParameterSetName = 'ReferenceID')] [string[]] $ReferenceID, [Parameter(Mandatory, ParameterSetName = 'Objects')] [ValidateSet( "ad_auth_service", "admingroup", "adminrole", "adminuser", "allendpoints", "allnsgroup", "allrecords", "allrpzrecords", "approvalworkflow", "authpolicy", "awsrte53taskgroup", "awsuser", "bfdtemplate", "bulkhost", "bulkhostnametemplate", "cacertificate", "capacityreport", "captiveportal", "certificate:authservice", "csvimporttask", "db_objects", "dbsnapshot", "ddns:principalcluster", "ddns:principalcluster:group", "deleted_objects", "dhcp:statistics", "dhcpfailover", "dhcpoptiondefinition", "dhcpoptionspace", "discovery", "discovery:credentialgroup", "discovery:device", "discovery:devicecomponent", "discovery:deviceinterface", "discovery:deviceneighbor", "discovery:devicesupportbundle", "discovery:diagnostictask", "discovery:gridproperties", "discovery:memberproperties", "discovery:sdnnetwork", "discovery:status", "discovery:vrf", "discoverytask", "distributionschedule", "dns64group", "dtc", "dtc:allrecords", "dtc:certificate", "dtc:lbdn", "dtc:monitor", "dtc:monitor:http", "dtc:monitor:icmp", "dtc:monitor:pdp", "dtc:monitor:sip", "dtc:monitor:snmp", "dtc:monitor:tcp", "dtc:object", "dtc:pool", "dtc:record:a", "dtc:record:aaaa", "dtc:record:cname", "dtc:record:naptr", "dtc:record:srv", "dtc:server", "dtc:topology", "dtc:topology:label", "dtc:topology:rule", "dxl:endpoint", "extensibleattributedef", "fileop", "filterfingerprint", "filtermac", "filternac", "filteroption", "filterrelayagent", "fingerprint", "fixedaddress", "fixedaddresstemplate", "ftpuser", "grid", "grid:cloudapi", "grid:cloudapi:cloudstatistics", "grid:cloudapi:tenant", "grid:cloudapi:vm", "grid:cloudapi:vmaddress", "grid:dashboard", "grid:dhcpproperties", "grid:dns", "grid:filedistribution", "grid:license_pool", "grid:license_pool_container", "grid:maxminddbinfo", "grid:member:cloudapi", "grid:servicerestart:group", "grid:servicerestart:group:order", "grid:servicerestart:request", "grid:servicerestart:request:changedobject", "grid:servicerestart:status", "grid:threatanalytics", "grid:threatprotection", "grid:x509certificate", "hostnamerewritepolicy", "hsm:allgroups", "hsm:safenetgroup", "hsm:thalesgroup", "ipam:statistics", "ipv4address", "ipv6address", "ipv6dhcpoptiondefinition", "ipv6dhcpoptionspace", "ipv6fixedaddress", "ipv6fixedaddresstemplate", "ipv6network", "ipv6networkcontainer", "ipv6networktemplate", "ipv6range", "ipv6rangetemplate", "ipv6sharednetwork", "kerberoskey", "ldap_auth_service", "lease", "license:gridwide", "localuser:authservice", "macfilteraddress", "mastergrid", "member", "member:dhcpproperties", "member:dns", "member:filedistribution", "member:license", "member:parentalcontrol", "member:threatanalytics", "member:threatprotection", "memberdfp", "msserver", "msserver:adsites:domain", "msserver:adsites:site", "msserver:dhcp", "msserver:dns", "mssuperscope", "namedacl", "natgroup", "network", "network_discovery", "networkcontainer", "networktemplate", "networkuser", "networkview", "notification:rest:endpoint", "notification:rest:template", "notification:rule", "nsgroup", "nsgroup:delegation", "nsgroup:forwardingmember", "nsgroup:forwardstubserver", "nsgroup:stubmember", "orderedranges", "orderedresponsepolicyzones", "outbound:cloudclient", "parentalcontrol:avp", "parentalcontrol:blockingpolicy", "parentalcontrol:subscriber", "parentalcontrol:subscriberrecord", "parentalcontrol:subscribersite", "permission", "pxgrid:endpoint", "radius:authservice", "range", "rangetemplate", "record:a", "record:aaaa", "record:alias", "record:caa", "record:cname", "record:dhcid", "record:dname", "record:dnskey", "record:ds", "record:dtclbdn", "record:host", "record:host_ipv4addr", "record:host_ipv6addr", "record:mx", "record:naptr", "record:ns", "record:nsec", "record:nsec3", "record:nsec3param", "record:ptr", "record:rpz:a", "record:rpz:a:ipaddress", "record:rpz:aaaa", "record:rpz:aaaa:ipaddress", "record:rpz:cname", "record:rpz:cname:clientipaddress", "record:rpz:cname:clientipaddressdn", "record:rpz:cname:ipaddress", "record:rpz:cname:ipaddressdn", "record:rpz:mx", "record:rpz:naptr", "record:rpz:ptr", "record:rpz:srv", "record:rpz:txt", "record:rrsig", "record:srv", "record:tlsa", "record:txt", "record:unknown", "recordnamepolicy", "request", "restartservicestatus", "rir", "rir:organization", "roaminghost", "ruleset", "saml:authservice", "scavengingtask", "scheduledtask", "search", "sharednetwork", "sharedrecord:a", "sharedrecord:aaaa", "sharedrecord:cname", "sharedrecord:mx", "sharedrecord:srv", "sharedrecord:txt", "sharedrecordgroup", "smartfolder:children", "smartfolder:global", "smartfolder:personal", "snmpuser", "superhost", "superhostchild", "syslog:endpoint", "tacacsplus:authservice", "taxii", "tftpfiledir", "threatanalytics:analytics_whitelist", "threatanalytics:moduleset", "threatanalytics:whitelist", "threatinsight:cloudclient", "threatprotection:grid:rule", "threatprotection:profile", "threatprotection:profile:rule", "threatprotection:rule", "threatprotection:rulecategory", "threatprotection:ruleset", "threatprotection:ruletemplate", "threatprotection:statistics", "upgradegroup", "upgradeschedule", "upgradestatus", "userprofile", "vdiscoverytask", "view", "vlan", "vlanrange", "vlanview", "zone_auth", "zone_auth_discrepancy", "zone_delegated", "zone_forward", "zone_rp", "zone_stub" )][string] $Object, [int] $MaxResults, [switch] $FetchFromSchema, [string[]] $ReturnFields ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxObjects - You must first connect to an Infoblox server using Connect-Infoblox' return } if ($Object) { Write-Verbose -Message "Get-InfobloxObjects - Requesting $Object" if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "$Object" } $invokeInfobloxQuerySplat = @{ RelativeUri = $Object.ToLower() Method = 'GET' QueryParameter = @{ _return_fields = $ReturnFields } } if ($MaxResults) { $invokeInfobloxQuerySplat.QueryParameter._max_results = $MaxResults } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -LastProperty '_ref' } else { foreach ($Ref in $ReferenceID) { Write-Verbose -Message "Get-InfobloxObjects - Requesting $Ref" if ($FetchFromSchema) { $ObjectType = $Ref.Split('/')[0] $ReturnFields = Get-FieldsFromSchema -SchemaObject "$ObjectType" if ($ReturnFields) { Write-Verbose -Message "Get-InfobloxObjects - Requesting $ObjectType with fields $ReturnFields" } else { Write-Warning -Message "Get-InfobloxObjects - Failed to get fields for $ObjectType" } } $invokeInfobloxQuerySplat = @{ RelativeUri = $Ref Method = 'GET' QueryParameter = @{ _return_fields = $ReturnFields } } if ($MaxResults) { $invokeInfobloxQuerySplat.QueryParameter._max_results = $MaxResults } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -LastProperty '_ref' } } } function Get-InfobloxPermission { [cmdletbinding()] param( [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxPermissions - You must first connect to an Infoblox server using Connect-Infoblox' return } # defalt return fields if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "permission" } $invokeInfobloxQuerySplat = @{ RelativeUri = 'permission' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = $ReturnFields } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -LastProperty '_ref' } function Get-InfobloxResponsePolicyZones { [cmdletbinding()] param( [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxResponsePolicyZones - You must first connect to an Infoblox server using Connect-Infoblox' return } # defalt return fields if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "zone_rp" } $invokeInfobloxQuerySplat = @{ RelativeUri = 'zone_rp' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = $ReturnFields } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -LastProperty '_ref' } function Get-InfobloxSchema { <# .SYNOPSIS Get the schema for Infoblox as a whole or a specific object .DESCRIPTION Get the schema for Infoblox as a whole or a specific object .PARAMETER Object The object to get the schema for .PARAMETER ReturnReadOnlyFields Return only read-only fields in format suitable for use with Invoke-InfobloxQuery .PARAMETER ReturnWriteFields Return only write fields in format suitable for use with Invoke-InfobloxQuery .PARAMETER ReturnFields Return all fields in full objects .EXAMPLE Get-InfobloxSchema .EXAMPLE Get-InfobloxSchema -Object 'record:host' .NOTES General notes #> [CmdletBinding()] param( [string] $Object, [switch] $ReturnReadOnlyFields, [switch] $ReturnWriteFields, [switch] $ReturnFields ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxSchema - You must first connect to an Infoblox server using Connect-Infoblox' return } if ($Object) { $invokeInfobloxQuerySplat = @{ RelativeUri = "$($Object.ToLower())" Method = 'Get' Query = @{ _schema = $true } } } else { $invokeInfobloxQuerySplat = @{ RelativeUri = "?_schema" Method = 'Get' } } $Query = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false if ($Query) { if ($ReturnReadOnlyFields) { Get-FieldsFromSchema -Schema $Query -SchemaObject $Object } elseif ($ReturnWriteFields) { $Fields = ((Get-InfobloxSchema -Object $Object).Fields | Where-Object { $_.supports -like "*r*" }).Name $Fields -join ',' } elseif ($ReturnFields) { (Get-InfobloxSchema -Object $Object).Fields } else { $Query } } else { Write-Warning -Message 'Get-InfobloxSchema - No schema returned' } } function Get-InfoBloxSearch { [CmdletBinding()] param( [parameter(ParameterSetName = 'IPv4')][string] $IPv4Address ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfoBloxSearch - You must first connect to an Infoblox server using Connect-Infoblox' return } Write-Verbose -Message "Get-InfoBloxSearch - Requesting IPv4Address [$IPv4Address]" $invokeInfobloxQuerySplat = [ordered]@{ RelativeUri = 'search' Method = 'GET' QueryParameter = [ordered]@{ _max_results = 1000000 } } if ($IPv4Address) { $invokeInfobloxQuerySplat.QueryParameter.address = $IPv4Address } Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false } function Get-InfobloxVDiscoveryTask { [cmdletbinding()] param( [switch] $FetchFromSchema ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Get-InfobloxVDiscoveryTask - You must first connect to an Infoblox server using Connect-Infoblox' return } # defalt return fields if ($FetchFromSchema) { $ReturnFields = Get-FieldsFromSchema -SchemaObject "vdiscoverytask" } $invokeInfobloxQuerySplat = @{ RelativeUri = 'vdiscoverytask' Method = 'GET' QueryParameter = @{ _max_results = 1000000 _return_fields = $ReturnFields } } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat -WhatIf:$false $Output | Select-ObjectByProperty -LastProperty '_ref' } function Invoke-InfobloxQuery { [CmdletBinding(SupportsShouldProcess)] param( [parameter(Mandatory)][string] $BaseUri, [parameter(Mandatory)][string] $RelativeUri, [parameter()][pscredential] $Credential, [Parameter()][Microsoft.PowerShell.Commands.WebRequestSession] $WebSession, [parameter()][System.Collections.IDictionary] $QueryParameter, [parameter()][string] $Method = 'GET', [parameter()][System.Collections.IDictionary] $Body ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Invoke-InfobloxQuery - You must first connect to an Infoblox server using Connect-Infoblox' return } if (-not $Credential -and -not $WebSession) { if ($ErrorActionPreference -eq 'Stop') { throw 'Invoke-InfobloxQuery - You must provide either a Credential or a WebSession with a cookie from Connect-Infoblox' } Write-Warning -Message 'Invoke-InfobloxQuery - You must provide either a Credential or a WebSession with a cookie from Connect-Infoblox' return } $joinUriQuerySplat = @{ BaseUri = $BaseUri RelativeOrAbsoluteUri = $RelativeUri } if ($QueryParameter) { $joinUriQuerySplat['QueryParameter'] = $QueryParameter } # if ($Method -eq 'GET') { # if (-not $QueryParameter) { # $joinUriQuerySplat['QueryParameter'] = [ordered] @{} # } # #_paging = 1 # #_return_as_object = 1 # #_max_results = 100000 # $joinUriQuerySplat['QueryParameter']._max_results = 1000000 # } $Url = Join-UriQuery @joinUriQuerySplat if ($PSCmdlet.ShouldProcess($Url, "Invoke-InfobloxQuery - $Method")) { Write-Verbose -Message "Invoke-InfobloxQuery - Querying $Url with $Method" # $WebSession = New-WebSession -Cookies @{ # timeout = 600 # mtime = 144631868 # client = 'API' # } -For $BaseUri try { $invokeRestMethodSplat = @{ Uri = $Url Method = $Method Credential = $Credential ContentType = 'application/json' ErrorAction = 'Stop' Verbose = $false WebSession = $WebSession #$PSDefaultParameterValues['Invoke-InfobloxQuery:WebSession'] TimeoutSec = 600 } if ($Body) { $invokeRestMethodSplat.Body = $Body | ConvertTo-Json -Depth 10 } if ($Script:InfobloxConfiguration['SkipCertificateValidation'] -eq $true) { $invokeRestMethodSplat.SkipCertificateCheck = $true } Remove-EmptyValue -Hashtable $invokeRestMethodSplat -Recursive -Rerun 2 Invoke-RestMethod @invokeRestMethodSplat # we connected to the server, so we can reset the default Credentials value $PSDefaultParameterValues['Invoke-InfobloxQuery:Credential'] = $null } catch { if ($PSVersionTable.PSVersion.Major -gt 5) { $OriginalError = $_.Exception.Message if ($_.ErrorDetails.Message) { try { $JSONError = ConvertFrom-Json -InputObject $_.ErrorDetails.Message -ErrorAction Stop } catch { if ($ErrorActionPreference -eq 'Stop') { throw $OriginalError } Write-Warning -Message "Invoke-InfobloxQuery - Querying $Url failed. Error: $OriginalError" return } if ($JSONError -and $JSONError.text) { if ($ErrorActionPreference -eq 'Stop') { throw $JSONError.text } Write-Warning -Message "Invoke-InfobloxQuery - Querying $Url failed. $($JSONError.text)" return } else { if ($ErrorActionPreference -eq 'Stop') { throw $OriginalError } Write-Warning -Message "Invoke-InfobloxQuery - Querying $Url failed. Error: $OriginalError" } } else { if ($ErrorActionPreference -eq 'Stop') { throw } Write-Warning -Message "Invoke-InfobloxQuery - Querying $Url failed. Error: $OriginalError" } } else { if ($ErrorActionPreference -eq 'Stop') { throw } Write-Warning -Message "Invoke-InfobloxQuery - Querying $Url failed. Error: $($_.Exception.Message)" } } } } function New-InfobloxOption { <# .SYNOPSIS Creates a dummy Infoblox option to use within other cmdlets .DESCRIPTION This function creates a new Infoblox option. It's just syntactic sugar to make it easier to create options to use within other cmdlets. .PARAMETER Name The name of the Infoblox option. This parameter is mandatory. .PARAMETER Number The number of the Infoblox option. This parameter is mandatory. .PARAMETER UseOption A switch indicating whether to use the option. This parameter is mandatory. .PARAMETER Value The value of the Infoblox option. This parameter is mandatory. .PARAMETER VendorClass The vendor class of the Infoblox option. This parameter is mandatory. .EXAMPLE $addInfobloxDHCPRangeSplat = @{ StartAddress = '10.10.12.5' EndAddress = '10.10.12.10' Options = @( New-InfobloxOption -Name "dhcp-lease-time" -Number 51 -UseOption -Value '86400' -VendorClass 'DHCP' New-InfobloxOption -Name "domain-name-servers" -Number 6 -UseOption -Value '192.168.0.15' -VendorClass 'DHCP' New-InfobloxOption -Name 'routers' -Number 3 -UseOption -Value '192.168.11.12' -VendorClass 'DHCP' New-InfobloxOption -Name 'time-servers' -Number 4 -UseOption -Value '11' -VendorClass 'DHCP' ) Verbose = $true } Add-InfobloxDHCPRange @addInfobloxDHCPRangeSplat .NOTES This function is used to create a dummy Infoblox option to use within other cmdlets. #> [CmdletBinding()] param( [Parameter(Mandatory)][string] $Name, [Parameter(Mandatory)][alias('Num')][int] $Number, [Parameter(Mandatory)][switch] $UseOption, [Parameter(Mandatory)][string] $Value, [Parameter(Mandatory)][string] $VendorClass ) [ordered] @{ "name" = $Name "num" = $Number "use_option" = $UseOption.IsPresent "value" = $Value "vendor_class" = $VendorClass } } function Remove-InfobloxDnsRecord { <# .SYNOPSIS Remove Infoblox DNS records .DESCRIPTION Remove Infoblox DNS records .PARAMETER Name Name of the record to remove .PARAMETER Type Type of the record to remove .PARAMETER SkipPTR Skip PTR record removal, when removing A record .PARAMETER LogPath Path to log file. Changes are logged to this file .EXAMPLE Remove-InfobloxDnsRecord -Name 'test.example.com' -Type 'A' -WhatIf .EXAMPLE Remove-InfobloxDnsRecord -Name 'test.example.com' -Type 'A' -SkipPTR -WhatIf .NOTES General notes #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)][string[]] $Name, [ValidateSet( 'A', 'CNAME', 'AAAA', 'PTR' )] [Parameter(Mandatory)][string] $Type, [switch] $SkipPTR, [string] $LogPath ) [Array] $ToBeDeleted = foreach ($Record in $Name) { $FoundRecord = Get-InfobloxDNSRecord -Name $Record -Type $Type -Verbose:$false if ($FoundRecord) { $FoundRecord if ($LogPath) { Write-Color -Text "Found $($FoundRecord.name) with type $Type to be removed" -LogFile $LogPath -NoConsoleOutput } } else { Write-Verbose -Message "Remove-InfobloxDnsRecord - No record for $Record were found. Skipping" if ($LogPath) { Write-Color -Text "No record for $Record were found. Skipping" -LogFile $LogPath -NoConsoleOutput } } } Write-Verbose -Message "Remove-InfobloxDnsRecord - Found $($ToBeDeleted.Count) records to delete" [Array] $ToBeDeletedPTR = @( if ($Type -eq 'A' -and -not $SkipPTR) { foreach ($Record in $ToBeDeleted) { if ($null -eq $Record.ipv4addr) { continue } try { $PTRAddress = Convert-IpAddressToPtrString -IPAddress $Record.ipv4addr -ErrorAction Stop } catch { Write-Warning -Message "Remove-InfobloxDnsRecord - Failed to convert $($Record.ipv4addr) to PTR" if ($LogPath) { Write-Color -Text "Failed to convert $($Record.ipv4addr) to PTR" -NoConsoleOutput -LogFile $LogPath } } if ($PTRAddress) { $PTRRecord = Get-InfobloxDNSRecord -Type PTR -Name $PTRAddress -Verbose:$false if ($PTRRecord) { $PTRRecord if ($LogPath) { Write-Color -Text "Found $($PTRRecord.name) with type PTR to be removed" -NoConsoleOutput -LogFile $LogPath } } else { Write-Verbose -Message "Remove-InfobloxDnsRecord - No PTR record for $($Record.name) were found. Skipping" if ($LogPath) { Write-Color -Text "No PTR record for $($Record.name) were found. Skipping" -NoConsoleOutput -LogFile $LogPath } } } } } ) if ($ToBeDeletedPTR.Count -gt 0) { Write-Verbose -Message "Remove-InfobloxDnsRecord - Found $($ToBeDeletedPTR.Count) PTR records to delete" } foreach ($Record in $ToBeDeleted) { if (-not $Record._ref) { Write-Warning -Message "Remove-InfobloxDnsRecord - Record does not have a reference ID, skipping" if ($LogPath) { Write-Color -Text "Record does not have a reference ID, skipping" -NoConsoleOutput -LogFile $LogPath } continue } Write-Verbose -Message "Remove-InfobloxDnsRecord - Removing $($Record.name) with type $Type / WhatIf:$WhatIfPreference" if ($LogPath) { Write-Color -Text "Removing $($Record.name) with type $Type" -NoConsoleOutput -LogFile $LogPath } try { $Success = Remove-InfobloxObject -ReferenceID $Record._ref -WhatIf:$WhatIfPreference -ErrorAction Stop -ReturnSuccess -Verbose:$false if ($Success -eq $true -or $WhatIfPreference) { Write-Verbose -Message "Remove-InfobloxDnsRecord - Removed $($Record.name) with type $Type / WhatIf: $WhatIfPreference" if ($LogPath) { Write-Color -Text "Removed $($Record.name) with type $Type" -NoConsoleOutput -LogFile $LogPath } } else { # this shouldn't really happen as the error action is set to stop Write-Warning -Message "Remove-InfobloxDnsRecord - Failed to remove $($Record.name) with type $Type / WhatIf: $WhatIfPreference" if ($LogPath) { Write-Color -Text "Failed to remove $($Record.name) with type $Type / WhatIf: $WhatIfPreference" -NoConsoleOutput -LogFile $LogPath } } } catch { Write-Warning -Message "Remove-InfobloxDnsRecord - Failed to remove $($Record.name) with type $Type, error: $($_.Exception.Message)" if ($LogPath) { Write-Color -Text "Failed to remove $($Record.name) with type $Type, error: $($_.Exception.Message)" -NoConsoleOutput -LogFile $LogPath } } } foreach ($Record in $ToBeDeletedPTR) { if (-not $Record._ref) { Write-Warning -Message "Remove-InfobloxDnsRecord - PTR record does not have a reference ID, skipping" if ($LogPath) { Write-Color -Text "PTR record does not have a reference ID, skipping" -NoConsoleOutput -LogFile $LogPath } continue } Write-Verbose -Message "Remove-InfobloxDnsRecord - Removing $($Record.name) with type PTR / WhatIf:$WhatIfPreference" if ($LogPath) { Write-Color -Text "Removing $($Record.name) with type PTR / WhatIf: $WhatIfPreference" -NoConsoleOutput -LogFile $LogPath } try { $Success = Remove-InfobloxObject -ReferenceID $Record._ref -WhatIf:$WhatIfPreference -ErrorAction Stop -ReturnSuccess -Verbose:$false if ($Success -eq $true -or $WhatIfPreference) { Write-Verbose -Message "Remove-InfobloxDnsRecord - Removed $($Record.name) with type PTR / WhatIf: $WhatIfPreference" if ($LogPath) { Write-Color -Text "Removed $($Record.name) with type PTR / WhatIf: $WhatIfPreference" -NoConsoleOutput -LogFile $LogPath } } else { # this shouldn't really happen as the error action is set to stop Write-Warning -Message "Remove-InfobloxDnsRecord - Failed to remove $($Record.name) with type PTR / WhatIf: $WhatIfPreference" if ($LogPath) { Write-Color -Text "Failed to remove $($Record.name) with type PTR / WhatIf: $WhatIfPreference" -NoConsoleOutput -LogFile $LogPath } } } catch { Write-Warning -Message "Remove-InfobloxDnsRecord - Failed to remove $($Record.name) with type PTR, error: $($_.Exception.Message)" if ($LogPath) { Write-Color -Text "Failed to remove $($Record.name) with type PTR, error: $($_.Exception.Message)" -NoConsoleOutput -LogFile $LogPath } } } } function Remove-InfobloxFixedAddress { [cmdletbinding(SupportsShouldProcess)] param( [parameter(Mandatory)][string] $MacAddress, [parameter()][string] $IPv4Address ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Remove-InfobloxFixedAddress - You must first connect to an Infoblox server using Connect-Infoblox' return } if (-not $IPv4Address) { Write-Verbose -Message "Remove-InfobloxFixedAddress - Removing $MacAddress" } else { Write-Verbose -Message "Remove-InfobloxFixedAddress - Removing $MacAddress from $IPv4Address" } $ListMacaddresses = Get-InfobloxFixedAddress -MacAddress $MacAddress if ($IPv4Address) { $ListMacaddresses = $ListMacaddresses | Where-Object -Property ipv4addr -EQ -Value $IPv4Address } foreach ($Mac in $ListMacaddresses) { $invokeInfobloxQuerySplat = @{ RelativeUri = "$($Mac._ref)" Method = 'DELETE' } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat #-WarningAction SilentlyContinue -WarningVariable varWarning if ($Output) { Write-Verbose -Message "Remove-InfobloxFixedAddress - Removed $($Mac.ipv4addr) with mac address $($Mac.mac) / $Output" } #else { #if (-not $WhatIfPreference) { # Write-Warning -Message "Remove-InfobloxFixedAddress - Failed to remove $($Mac.ipv4addr) with mac address $($Mac.mac), error: $varWarning" #} #} } } function Remove-InfobloxIPAddress { <# .SYNOPSIS Removes an IP address from Infoblox. .DESCRIPTION This function removes an IP address from Infoblox. It checks for an existing connection to an Infoblox server, established via Connect-Infoblox, before attempting the removal. The function supports verbose output for detailed operation insights. .PARAMETER IPv4Address The IPv4 address to be removed from Infoblox. This parameter is mandatory. .EXAMPLE Remove-InfobloxIPAddress -IPv4Address '192.168.1.100' Removes the IP address 192.168.1.100 from Infoblox. .NOTES Ensure you are connected to an Infoblox server using Connect-Infoblox before executing this function. The function uses Write-Verbose for detailed operation output, which can be enabled by setting $VerbosePreference or using the -Verbose switch. #> [cmdletbinding(SupportsShouldProcess)] param( [parameter()][string] $IPv4Address ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Remove-InfobloxIPAddress - You must first connect to an Infoblox server using Connect-Infoblox' return } Write-Verbose -Message "Remove-InfobloxIPAddress - Removing $IPv4Address" $ListIP = Get-InfobloxIPAddress -IPv4Address $IPv4Address foreach ($IP in $ListIP) { $invokeInfobloxQuerySplat = @{ RelativeUri = "$($IP._ref)" Method = 'DELETE' } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat if ($Output) { Write-Verbose -Message "Remove-InfobloxIPAddress - Removed $($IP.ip_address) from network $($IP.network) / $Output" } } } function Remove-InfobloxNetworkExtensibleAttribute { <# .SYNOPSIS Removes an extensible attribute from a specified network in Infoblox. .DESCRIPTION This function removes an extensible attribute from a network in Infoblox. It requires an established connection to an Infoblox server, which can be done using the Connect-Infoblox function. The function checks if the specified network exists before attempting to remove the extensible attribute. .PARAMETER Network The network from which the extensible attribute will be removed. This parameter is mandatory. .PARAMETER Attribute The name of the extensible attribute to remove. This parameter is mandatory. .EXAMPLE Remove-InfobloxNetworkExtensibleAttribute -Network '192.168.1.0/24' -Attribute 'Location' Removes the 'Location' extensible attribute from the network '192.168.1.0/24'. .NOTES You must first connect to an Infoblox server using Connect-Infoblox before running this function. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)][alias('Subnet')][string] $Network, [Parameter(Mandatory)][alias('ExtensibleAttribute')][string] $Attribute ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Remove-InfobloxNetworkExtensibleAttribute - You must first connect to an Infoblox server using Connect-Infoblox' return } $NetworkInformation = Get-InfobloxNetwork -Network $Network if (-not $NetworkInformation._ref) { Write-Warning -Message "Remove-InfobloxNetworkExtensibleAttribute - Network $Network not found" return } $Body = [ordered] @{ "extattrs-" = @{ $Attribute = @{} } } Remove-EmptyValue -Hashtable $Body $invokeInfobloxQuerySplat = @{ RelativeUri = $NetworkInformation._ref Method = 'PUT' Body = $Body } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat if ($Output) { Write-Verbose -Message "Remove-InfobloxNetworkExtensibleAttribute - $Output" if ($ReturnOutput) { $Output } } } function Remove-InfobloxObject { <# .SYNOPSIS Remove an Infoblox object by reference ID .DESCRIPTION Remove an Infoblox object by reference ID It can be used to remove any object type, but it is recommended to use the more specific cmdlets .PARAMETER Objects An array of objects to remove .PARAMETER ReferenceID The reference ID of the object to remove .EXAMPLE Remove-InfobloxObject -ReferenceID 'record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmNvbS5pbmZvLmhvc3Q6MTcyLjI2LjEuMjAu:' .NOTES General notes #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ReferenceID')] param( [Parameter(Mandatory, ParameterSetName = 'Array')][Array] $Objects, [parameter(Mandatory, ParameterSetName = 'ReferenceID')][string] $ReferenceID, [switch] $ReturnSuccess ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Remove-InfobloxObject - You must first connect to an Infoblox server using Connect-Infoblox' return } if ($Objects) { $Objects | ForEach-Object { if ($_._ref) { $ReferenceID = $_._ref Remove-InfobloxObject -ReferenceID $ReferenceID } else { Write-Warning -Message "Remove-InfobloxObject - Object does not have a reference ID: $_" } } } else { $invokeInfobloxQuerySplat = @{ RelativeUri = $ReferenceID Method = 'DELETE' } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat #-WarningAction SilentlyContinue -WarningVariable varWarning if ($Output) { Write-Verbose -Message "Remove-InfobloxObject - Removed $($ReferenceID) / $Output" if ($ReturnSuccess) { $true } } else { if ($ReturnSuccess) { $false } } } } function Set-InfobloxDNSRecord { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER ReferenceID Parameter description .PARAMETER Object Parameter description .PARAMETER Type Parameter description .EXAMPLE Set-InfobloxDNSRecord -ReferenceID 'record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmNvbS5teW5ldC5jb20kMTAuMTAuMTAuMTA=' -Name 'xyz' -Type 'A' .EXAMPLE Set-InfobloxDNSRecord -ReferenceID 'record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmNvbS5teW5ldC5jb20kMTAuMTAuMTAuMTA=' -PTRName 'xyz -Type 'PTR' .EXAMPLE Set-InfobloxDNSRecord -ReferenceID 'record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmNvbS5teW5ldC5jb20kMTAuMTAuMTAuMTA=' -Name 'test2.mcdonalds.com' -Type 'CNAME' .NOTES General notes #># [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ReferenceID')] param( [parameter(ParameterSetName = 'ReferenceID', Mandatory)][string] $ReferenceID, [Alias("Name", 'PtrName', 'PTR', 'NameServer', 'Text')][parameter(ParameterSetName = 'Object', Mandatory)][string] $Object, [parameter(Mandatory)][ValidateSet( 'A', 'AAAA', 'CNAME', 'HOST', 'PTR', 'MX', 'NS', 'TXT' )][string] $Type ) if (-not $Script:InfobloxConfiguration) { if ($ErrorActionPreference -eq 'Stop') { throw 'You must first connect to an Infoblox server using Connect-Infoblox' } Write-Warning -Message 'Set-InfobloxDNSRecord - You must first connect to an Infoblox server using Connect-Infoblox' return } # Lets convert it to lowercase, since Infoblox is case sensitive $Type = $Type.ToLower() if ($Type -in 'A', 'AAAA', 'HOST', 'CNAME', 'MX') { $Body = [ordered] @{ name = $Object.ToLower() } } elseif ($Type -eq 'PTR') { $Body = [ordered] @{ ptrdname = $Object.ToLower() } } elseif ($Type -eq 'NS') { $Body = [ordered] @{ nameserver = $Object.ToLower() } } elseif ($Type -eq 'TXT') { $Body = [ordered] @{ text = $Object.ToLower() } } else { if ($ErrorActionPreference -eq 'Stop') { throw "Set-InfobloxDNSRecord - Unsupported type: $Type" } Write-Warning -Message "Set-InfobloxDNSRecord - Unsupported type: $Type" return } $invokeInfobloxQuerySplat = @{ RelativeUri = $ReferenceID Method = 'PUT' Body = $Body } $Output = Invoke-InfobloxQuery @invokeInfobloxQuerySplat #-WarningAction SilentlyContinue -WarningVariable varWarning if ($Output) { Write-Verbose -Message "Set-InfobloxDNSRecord - Modified $Type / $Output" } #else { #if (-not $WhatIfPreference) { # Write-Warning -Message "Set-InfobloxDNSRecord - Failed to modify $Type, error: $varWarning" #} #} } # Export functions and aliases as required Export-ModuleMember -Function @('Add-InfobloxDHCPRange', 'Add-InfoBloxDNSRecord', 'Add-InfobloxFixedAddress', 'Add-InfobloxNetwork', 'Add-InfobloxNetworkExtensibleAttribute', 'Connect-Infoblox', 'Disconnect-Infoblox', 'Get-InfobloxDHCPLease', 'Get-InfobloxDHCPRange', 'Get-InfobloxDiscoveryTask', 'Get-InfobloxDNSAuthZone', 'Get-InfobloxDNSDelegatedZone', 'Get-InfobloxDNSForwardZone', 'Get-InfobloxDNSRecord', 'Get-InfobloxDNSRecordAll', 'Get-InfobloxDNSView', 'Get-InfobloxFixedAddress', 'Get-InfobloxGrid', 'Get-InfobloxIPAddress', 'Get-InfobloxMember', 'Get-InfobloxNetwork', 'Get-InfobloxNetworkNextAvailableIP', 'Get-InfobloxNetworkView', 'Get-InfobloxObjects', 'Get-InfobloxPermission', 'Get-InfobloxResponsePolicyZones', 'Get-InfobloxSchema', 'Get-InfoBloxSearch', 'Get-InfobloxVDiscoveryTask', 'Invoke-InfobloxQuery', 'New-InfobloxOption', 'Remove-InfobloxDnsRecord', 'Remove-InfobloxFixedAddress', 'Remove-InfobloxIPAddress', 'Remove-InfobloxNetworkExtensibleAttribute', 'Remove-InfobloxObject', 'Set-InfobloxDNSRecord') -Alias @('Add-InfobloxSubnet', 'Get-InfobloxDHCPLeases', 'Get-InfobloxDNSAuthZones', 'Get-InfobloxDNSRecords', 'Get-InfobloxDNSRecordsAll') # SIG # Begin signature block # MIItsQYJKoZIhvcNAQcCoIItojCCLZ4CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDuaRc8FZGNfP4E # tXwrmjXzDAxolSomJKZR9TaSAl73GqCCJrQwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggWQMIIDeKADAgECAhAFmxtXno4hMuI5B72nd3VcMA0GCSqG # SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH # NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIw # aTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLK # EdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4Tm # dDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembu # d8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnD # eMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1 # XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVld # QnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTS # YW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSm # M9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzT # QRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kx # fgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD # VR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzANBgkq # hkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNkaA9Wz3eucPn9mkqZucl4 # XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjSPMFDQK4dUPVS/JA7u5iZ # aWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK7VB6fWIhCoDIc2bRoAVg # X+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eBcg3AFDLvMFkuruBx8lbk # apdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp5aPNoiBB19GcZNnqJqGL # FNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msgdDDS4Dk0EIUhFQEI6FUy # 3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vriRbgjU2wGb2dVf0a1TD9u # KFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ79ARj6e/CVABRoIoqyc54 # zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5nLGbsQAe79APT0JsyQq8 # 7kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3i0objwG2J5VT6LaJbVu8 # aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0HEEcRrYc9B9F1vM/zZn4w # ggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH # NDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVT # MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1 # c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqG # SIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbS # g9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9 # /UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXn # HwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0 # VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4f # sbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40Nj # gHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0 # QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvv # mz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T # /jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk # 42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5r # mQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E # FgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5n # P+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcG # CCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu # Y29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln # aUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8v # Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNV # HSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIB # AH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxp # wc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIl # zpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQ # cAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfe # Kuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+j # Sbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJsh # IUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6 # OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDw # N7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR # 81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2 # VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGsDCCBJigAwIBAgIQ # CK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQGEwJVUzEV # MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t # MSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjEwNDI5MDAw # MDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln # aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT # aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjANBgkqhkiG9w0BAQEF # AAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zrPYGXcMW7xIUmMJ+k # jmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHMgQM+TXAkZLON4gh9 # NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8IrgnQnAZaf6mIBJNYc9 # URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyCEUhSaN4QvRRXXegY # E2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0p6MDDnSlrzm2q2AS # 4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQakhCBj7A7CdfHmzJa # wv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0XLyTRSiDNipmKF+w # c86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960IHnWmZcy740hQ83eR # Gv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2FKZbS110YU0/EpF2 # 3r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBHX8mBUHOFECMhWWCK # ZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q27IwyCQLMbDwMVhEC # AwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGg34Ou2 # O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P # MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB3BggrBgEFBQcB # AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr # BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1 # c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwHAYDVR0gBBUwEzAH # BgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIBADojRD2NCHbuj7w6 # mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6jfCbVN7w6XUhtldU/ # SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmImoqKwba9oUgYftzY # gBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtfJqGVWEjVGv7XJz/9 # kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrxoj7bQ7gzyE84FJKZ # 9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3LIU/Gs4m6Ri+kAew # Q3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx4b6cpwoG1iZnt5Lm # Tl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9Oj9FpsToFpFSi0HA # SIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+ICw2/O/TOHnuO77Xr # y7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug0wcCampAMEhLNKhR # ILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5Vzu0nAPthkX0tGFu # v2jiJmCG6sivqf6UHedjGzqGVnhOMIIGwjCCBKqgAwIBAgIQBUSv85SdCDmmv9s/ # X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln # aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5 # NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0MTAx # MzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu # MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwvSKOX # ejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsvlmbj # aedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4ZGIX7 # ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RChG4PB # uOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDaY7Mu # 6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S769Sg # LDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0rRUG # FOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQfqZc # ClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7UjipmAmh # cbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1Pt7U2 # 0clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM604qD # y0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAW # BgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglg # hkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0O # BBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6 # Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEy # NTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUF # BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6 # Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZT # SEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa1t6g # qbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1Ott5s # 1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4JZ0q # BXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvPnPp4 # 4pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3NG6w # QSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7MmJ4Z # iQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOGv6wn # LEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5Adza # ROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2nyMpqy # 0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0eaHDA # dwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9nR2Xl # G3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMIIHXzCCBUegAwIBAgIQB8JSdCgU # otar/iTqF+XdLjANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UE # ChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQg # Q29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTIzMDQxNjAw # MDAwMFoXDTI2MDcwNjIzNTk1OVowZzELMAkGA1UEBhMCUEwxEjAQBgNVBAcMCU1p # a2/FgsOzdzEhMB8GA1UECgwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMSEwHwYD # VQQDDBhQcnplbXlzxYJhdyBLxYJ5cyBFVk9URUMwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCUmgeXMQtIaKaSkKvbAt8GFZJ1ywOH8SwxlTus4McyrWmV # OrRBVRQA8ApF9FaeobwmkZxvkxQTFLHKm+8knwomEUslca8CqSOI0YwELv5EwTVE # h0C/Daehvxo6tkmNPF9/SP1KC3c0l1vO+M7vdNVGKQIQrhxq7EG0iezBZOAiukNd # GVXRYOLn47V3qL5PwG/ou2alJ/vifIDad81qFb+QkUh02Jo24SMjWdKDytdrMXi0 # 235CN4RrW+8gjfRJ+fKKjgMImbuceCsi9Iv1a66bUc9anAemObT4mF5U/yQBgAuA # o3+jVB8wiUd87kUQO0zJCF8vq2YrVOz8OJmMX8ggIsEEUZ3CZKD0hVc3dm7cWSAw # 8/FNzGNPlAaIxzXX9qeD0EgaCLRkItA3t3eQW+IAXyS/9ZnnpFUoDvQGbK+Q4/bP # 0ib98XLfQpxVGRu0cCV0Ng77DIkRF+IyR1PcwVAq+OzVU3vKeo25v/rntiXCmCxi # W4oHYO28eSQ/eIAcnii+3uKDNZrI15P7VxDrkUIc6FtiSvOhwc3AzY+vEfivUkFK # RqwvSSr4fCrrkk7z2Qe72Zwlw2EDRVHyy0fUVGO9QMuh6E3RwnJL96ip0alcmhKA # BGoIqSW05nXdCUbkXmhPCTT5naQDuZ1UkAXbZPShKjbPwzdXP2b8I9nQ89VSgQID # AQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYD # VR0OBBYEFHrxaiVZuDJxxEk15bLoMuFI5233MA4GA1UdDwEB/wQEAwIHgDATBgNV # HSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3Js # My5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQw # OTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAy # MUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEFBQcCARYbaHR0 # cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGUBggrBgEFBQcBAQSBhzCBhDAkBggr # BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBo # dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2Rl # U2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqG # SIb3DQEBCwUAA4ICAQC3EeHXUPhpe31K2DL43Hfh6qkvBHyR1RlD9lVIklcRCR50 # ZHzoWs6EBlTFyohvkpclVCuRdQW33tS6vtKPOucpDDv4wsA+6zkJYI8fHouW6Tqa # 1W47YSrc5AOShIcJ9+NpNbKNGih3doSlcio2mUKCX5I/ZrzJBkQpJ0kYha/pUST2 # CbE3JroJf2vQWGUiI+J3LdiPNHmhO1l+zaQkSxv0cVDETMfQGZKKRVESZ6Fg61b0 # djvQSx510MdbxtKMjvS3ZtAytqnQHk1ipP+Rg+M5lFHrSkUlnpGa+f3nuQhxDb7N # 9E8hUVevxALTrFifg8zhslVRH5/Df/CxlMKXC7op30/AyQsOQxHW1uNx3tG1DMgi # zpwBasrxh6wa7iaA+Lp07q1I92eLhrYbtw3xC2vNIGdMdN7nd76yMIjdYnAn7r38 # wwtaJ3KYD0QTl77EB8u/5cCs3ShZdDdyg4K7NoJl8iEHrbqtooAHOMLiJpiL2i9Y # n8kQMB6/Q6RMO3IUPLuycB9o6DNiwQHf6Jt5oW7P09k5NxxBEmksxwNbmZvNQ65Z # n3exUAKqG+x31Egz5IZ4U/jPzRalElEIpS0rgrVg8R8pEOhd95mEzp5WERKFyXhe # 6nB6bSYHv8clLAV0iMku308rpfjMiQkqS3LLzfUJ5OHqtKKQNMLxz9z185UCszGC # BlMwggZPAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ # bmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBS # U0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQB8JSdCgUotar/iTqF+XdLjANBglghkgB # ZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJ # AzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8G # CSqGSIb3DQEJBDEiBCABuCrgLj97KiFknv4Jp0WUfIaGgeB8omuS0ZwnB4nU/TAN # BgkqhkiG9w0BAQEFAASCAgAyr97zw3/ellMQnCqJajvrIH+QAQZmF2DC+m36n+rm # z3jSzDs0Jrq+lU5BLgQUAtkwzoWD67rTCZg9FiJY1TyZk0pBODjFpYswArG+WuaU # 7dNZZk6OnAbKhtCg5n/stR8Vppafmp4pZ9ItElOeGTwOBkrqqDy4yWndqqfaFcfk # t58r3zmuBNqU3UAO14ZV7RTuB0Hz1ylqgcGByaTYza//P9KnCZZv0xaFiol6gUS/ # 5ST4brKbR7alfy0i3p+d0L8kYcWiLpgxN2+xl0/qZO2KU1dnlyrtyhdoSY+oa0c1 # 3tYX3vTvYHz5VuU2b33D5MOph8K8KUCgnD6OQTXcOzNkwGrVhuJ/qnnvSWTe7ir8 # DBwJmyR4ixDDexrDsPmmaUrVZn5uyGxuVZL0vZkqGkUS3gsSON/jK0uL6SmF/ZC4 # T5NEqg2ePOfpr5TTiWIPnMcLd4JRezhKJ2BZBrpmtRs0Oql7nsAFVv4PzRpoJySZ # az28RrhblblI/EVLa86iZgjgNqzbg+/pvww7PeN5h3Ds6Kxbdl9ghOeUJNaUQpzG # rz9CKQkbdSRol40IOGe2Stx8V2Vz3YxSp8ybTZG2DRIlZcENBBdhAczAYp4AbHuG # weR35M7CvtS65FX1S7dicXCkbVNY361txSrEvfqUOn9oAp1JuURsKZSoMYR5oNLq # n6GCAyAwggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYTAlVT # MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1 # c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAVEr/OUnQg5 # pr/bP1/lYRYwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN # AQcBMBwGCSqGSIb3DQEJBTEPFw0yNDA2MTMxMjI5MDZaMC8GCSqGSIb3DQEJBDEi # BCD06S+CPIRO2GK7ZmR4yP0AUR3w3X/kM651spqR+gK5vzANBgkqhkiG9w0BAQEF # AASCAgAdW5eu+ehliBM871jYNSJtiy+qE2m3mScE7OG9I4u5tHtSZGO5mmrubONy # /mlYjlOe40M/TqsA5pBe/FAR594+OlatVoPnWZgrsSaJkCSok7qeSBDUZzbj+880 # T/z08l4FYoGGQSVwxXPxRC1+wGU/7mHeCOjyND7Ao8URpGVSA7cbAOQgdgZjxH5n # XQbXJwQX4rFx51uKJwhVQLnSGz5PYuQC+i1/PA4oqilm3s1pXrwPHynQu4W6/Fdl # PNQ3AaA2NIeqwNCaEFAMz55xOXrn1Rk9LfWfMFO7x6Mh2Pd43w15Omkqk287Pmos # cKr9tYvNhWaY/wNqzupHXaOydoz+qEpVliBlGP4RxyPXrXgCOJ2SmkoHkwoPc7Mb # Vd4kRCC0sBbbkHVmXK3czujZhmot3t+fu1AdKAAlcMyGM1i1xFTWeujdalzQvcGs # Y3QtYHNe4DzD1ne8+mR6auQLHUAuFi9dmnXBzwCcFv74vun8RN0DFsWelcT5GtiH # Tz1VJKlXURbfDhg3MnWyvXlI4N8J1r8YoadII4Uq1VG76IG3PvNaZMk1poEJ8Coj # 4CZusX27tcymlOBIjBEyDsv6jbF0WSiA3iCrjSve6UKrMMQO40i0LM7KifDHd20d # JWcwrwklzWWcKo8a5FQbkhHK64TbJKC+X9zZ6JfeFWJv0l6rZw== # SIG # End signature block |