-
Notifications
You must be signed in to change notification settings - Fork 0
/
12_Final_Get-AzSRFreeIpAddress.ps1
211 lines (178 loc) · 7.53 KB
/
12_Final_Get-AzSRFreeIpAddress.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
<#
.SYNOPSIS
Find free IP addresses in a Azure Virtual Network
.DESCRIPTION
Returns a specified number or all free IP addresses from a specified Azure Virtual Network
This allows easy IP address management if a static IP address should be used
This function is differnt to Test-AzureRmPrivateIPAddressAvailability since it returns all free IP addresses or a specified number.
There is also no need to provide a IP address
.PARAMETER NetworkName
Name of the Virtual Network
.PARAMETER ResourceGroupName
Name of the Resource Group
.PARAMETER SubnetName
Name of the subnet
.PARAMETER First
Gets only the specified number of objects. Enter the number of objects to get.
.EXAMPLE
Find all free IP addresses in a specifiec Virtual Network
Get-AzSRFreeIpAddress -NetworkName "MyVirtualNetwork" -ResourceGroupName "Subnet01"
.EXAMPLE
Find one free IP address in one of the available virtual networks
Get-AzVirtualNetwork | Get-AzSRFreeIpAddress -First 1
.NOTES
Copyright: (c) 2018 Fabian Bader
License: MIT https://opensource.org/licenses/MIT
#>
function Get-AzSRFreeIpAddress {
[CmdletBinding()]
param (
[Alias('Name')]
[Parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true)]
[String]$NetworkName,
[Parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true)]
[String]$ResourceGroupName,
[Parameter(Mandatory = $false)]
[String]$SubnetName,
[Int32]$First
)
Begin {
#region Check if logged in
try {
Get-AzureRmContext | Out-Null
}
catch {
throw $($_.Exception.Message)
}
#endregion
}
Process {
$FoundIPAddress = 0
# Check for Az Module
if ( ( Get-Module Az.Network -ListAvailable -ErrorAction SilentlyContinue ) ) {
$AzModule = $true
}
else {
$AzModule = $false
}
if ($SubnetName) {
if ($AzModule) {
$SubnetConfiguration = Get-AzVirtualNetwork -ExpandResource "subnets/ipConfigurations" -Name $NetworkName -ResourceGroupName $ResourceGroupName | Select-Object -ExpandProperty Subnets | Where-Object { $_.Name -eq $SubnetName}
}
else {
$SubnetConfiguration = Get-AzureRmVirtualNetwork -ExpandResource "subnets/ipConfigurations" -Name $NetworkName -ResourceGroupName $ResourceGroupName | Select-Object -ExpandProperty Subnets | Where-Object { $_.Name -eq $SubnetName}
}
}
else {
if ($AzModule) {
$SubnetConfiguration = Get-AzVirtualNetwork -ExpandResource "subnets/ipConfigurations" -Name $NetworkName -ResourceGroupName $ResourceGroupName | Select-Object -ExpandProperty Subnets
}
else {
$SubnetConfiguration = Get-AzureRmVirtualNetwork -ExpandResource "subnets/ipConfigurations" -Name $NetworkName -ResourceGroupName $ResourceGroupName | Select-Object -ExpandProperty Subnets
}
}
foreach ($Subnet in $SubnetConfiguration) {
Write-Verbose "Check for free IP address in subnet:`t$($Subnet.Name)"
if ( ( $PSBoundParameters.ContainsKey('First') ) -and ($FoundIPAddress -ge $First)) {
# We have enough IP addresses
break
}
Write-Verbose "Subnet address prefix:`t`t`t$($Subnet.AddressPrefix)"
$PossibleIpAddresses = Get-SubnetAddress -Subnet "$($Subnet.AddressPrefix)" | Select-Object -ExpandProperty IPAddressToString
$UsedIpAddresses = $Subnet.ipConfigurations.privateIPAddress
if ([string]::IsNullOrEmpty($UsedIpAddresses)) {
Write-Verbose "No IP addresses used"
$FreeIPAddresses = $PossibleIpAddresses
}
else {
$FreeIPAddresses = Compare-Object -ReferenceObject $PossibleIpAddresses -DifferenceObject $UsedIpAddresses | Where-Object { $_.SideIndicator -eq "<="} | Select-Object -ExpandProperty InputObject
}
Write-Verbose "Found $($FreeIPAddresses.Count) free IP addresses"
foreach ($IPAddress in $FreeIPAddresses) {
# First three address are reserved addresses in each subnet according to MSFT
# https://docs.microsoft.com/en-us/azure/virtual-network/virtual-networks-faq#are-there-any-restrictions-on-using-ip-addresses-within-these-subnets
if ($IPAddress -notin ( $PossibleIpAddresses | Select-Object -First 3 ) ) {
Write-Verbose "$IPAddress is free"
New-Object psobject -Property @{
"IPAddress" = $IPAddress
"Subnet" = $Subnet.Name
"VirtualNetworkName" = $NetworkName
"ResourceGroupName" = $ResourceGroupName
}
$FoundIPAddress++
if ( ( $PSBoundParameters.ContainsKey('First') ) -and ($FoundIPAddress -ge $First)) {
# We have enough IP addresses
break
}
}
else {
Write-Verbose "$IPAddress is reserved"
}
}
}
}
}
function Get-SubnetAddress {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]
$Subnet
)
# Website: https://powershell.org/forums/topic/ip-address-math/
# Copyright: (c) 2014 Dave Wyatt
$ipaddress = $null
# Validating the string format here instead of in a ValidateScript block allows us to use the
# $ipaddress and $matches variables without having to perform the parsing twice.
if ($Subnet -notmatch '^(?<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/(?<mask>\d{1,2})$') {
throw "Subnet address '$Subnet' does not match the expected CIDR format (example: 192.168.0.0/24)"
}
if (-not [ipaddress]::TryParse($matches['address'], [ref]$ipaddress)) {
throw "Subnet address '$Subnet' contains an invalid IPv4 address."
}
$maskDecimal = [int]$matches['mask']
if ($maskDecimal -gt 30) {
throw "Subnet address '$Subnet' contains an invalid subnet mask (must be less than or equal to 30)."
}
$hostBitCount = 32 - $maskDecimal
$netMask = [UInt32]0xFFFFFFFFL -shl $hostBitCount
$hostMask = -bnot $netMask
$networkAddress = (Get-UInt32FromIPAddress -IPAddress $ipaddress) -band $netMask
$broadcastAddress = $networkAddress -bor $hostMask
for ($address = $networkAddress + 1; $address -lt $broadcastAddress; $address++) {
Get-IPAddressFromUInt32 -UInt32 $address
}
}
function Get-IPAddressFromUInt32 {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[UInt32]
$UInt32
)
# Website: https://powershell.org/forums/topic/ip-address-math/
# Copyright: (c) 2014 Dave Wyatt
$bytes = [BitConverter]::GetBytes($UInt32)
if ([BitConverter]::IsLittleEndian) {
[Array]::Reverse($bytes)
}
return New-Object ipaddress(, $bytes)
}
function Get-UInt32FromIPAddress {
[OutputType('System.UInt32')]
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ipaddress]
$IPAddress
)
# Website: https://powershell.org/forums/topic/ip-address-math/
# Copyright: (c) 2014 Dave Wyatt
$bytes = $IPAddress.GetAddressBytes()
if ([BitConverter]::IsLittleEndian) {
[Array]::Reverse($bytes)
}
return [BitConverter]::ToUInt32($bytes, 0)
}