1 #!powershell
2
3 # Copyright: (c) 2018, Varun Chopra (@chopraaa) <v@chopraaa.com>
4 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6 #AnsibleRequires -CSharpUtil Ansible.Basic
7 #AnsibleRequires -OSVersion 6.2
8
9 Set-StrictMode -Version 2
10
11 $ErrorActionPreference = "Stop"
12
13 $spec = @{
14 options = @{
15 state = @{ type = "str"; choices = "absent", "present"; default = "present" }
16 drive_letter = @{ type = "str" }
17 disk_number = @{ type = "int" }
18 partition_number = @{ type = "int" }
19 partition_size = @{ type = "str" }
20 read_only = @{ type = "bool" }
21 active = @{ type = "bool" }
22 hidden = @{ type = "bool" }
23 offline = @{ type = "bool" }
24 mbr_type = @{ type = "str"; choices = "fat12", "fat16", "extended", "huge", "ifs", "fat32" }
25 gpt_type = @{ type = "str"; choices = "system_partition", "microsoft_reserved", "basic_data", "microsoft_recovery" }
26 }
27 supports_check_mode = $true
28 }
29
30 $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
31
32 $state = $module.Params.state
33 $drive_letter = $module.Params.drive_letter
34 $disk_number = $module.Params.disk_number
35 $partition_number = $module.Params.partition_number
36 $partition_size = $module.Params.partition_size
37 $read_only = $module.Params.read_only
38 $active = $module.Params.active
39 $hidden = $module.Params.hidden
40 $offline = $module.Params.offline
41 $mbr_type = $module.Params.mbr_type
42 $gpt_type = $module.Params.gpt_type
43
44 $size_is_maximum = $false
45 $ansible_partition = $false
46 $ansible_partition_size = $null
47 $partition_style = $null
48
49 $gpt_styles = @{
50 system_partition = "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"
51 microsoft_reserved = "{e3c9e316-0b5c-4db8-817d-f92df00215ae}"
52 basic_data = "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
53 microsoft_recovery = "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}"
54 }
55
56 $mbr_styles = @{
57 fat12 = 1
58 fat16 = 4
59 extended = 5
60 huge = 6
61 ifs = 7
62 fat32 = 12
63 }
64
Convert-SizeToBytesnull65 function Convert-SizeToBytes {
66 param(
67 $Size,
68 $Units
69 )
70
71 switch ($Units) {
72 "B" { return 1 * $Size }
73 "KB" { return 1000 * $Size }
74 "KiB" { return 1024 * $Size }
75 "MB" { return [Math]::Pow(1000, 2) * $Size }
76 "MiB" { return [Math]::Pow(1024, 2) * $Size }
77 "GB" { return [Math]::Pow(1000, 3) * $Size }
78 "GiB" { return [Math]::Pow(1024, 3) * $Size }
79 "TB" { return [Math]::Pow(1000, 4) * $Size }
80 "TiB" { return [Math]::Pow(1024, 4) * $Size }
81 }
82 }
83
84 if ($null -ne $partition_size) {
85 if ($partition_size -eq -1) {
86 $size_is_maximum = $true
87 }
88 elseif ($partition_size -match '^(?<Size>[0-9]+)[ ]*(?<Units>b|kb|kib|mb|mib|gb|gib|tb|tib)$') {
89 $ansible_partition_size = Convert-SizeToBytes -Size $Matches.Size -Units $Matches.Units
90 }
91 else {
92 $module.FailJson("Invalid partition size. B, KB, KiB, MB, MiB, GB, GiB, TB, TiB are valid partition size units")
93 }
94 }
95
96 # If partition_exists, we can change or delete it; otherwise we only need the disk to create a new partition
97 if ($null -ne $disk_number -and $null -ne $partition_number) {
98 $ansible_partition = Get-Partition -DiskNumber $disk_number -PartitionNumber $partition_number -ErrorAction SilentlyContinue
99 }
100 # Check if drive_letter is either auto-assigned or a character from A-Z
101 elseif ($drive_letter -and -not ($disk_number -and $partition_number)) {
102 if ($drive_letter -eq "auto" -or $drive_letter -match "^[a-zA-Z]$") {
103 $ansible_partition = Get-Partition -DriveLetter $drive_letter -ErrorAction SilentlyContinue
104 }
105 else {
106 $module.FailJson("Incorrect usage of drive_letter: specify a drive letter from A-Z or use 'auto' to automatically assign a drive letter")
107 }
108 }
109 elseif ($disk_number) {
110 try {
111 Get-Disk -Number $disk_number | Out-Null
112 } catch {
113 $module.FailJson("Specified disk does not exist")
114 }
115 }
116 else {
117 $module.FailJson("You must provide disk_number, partition_number")
118 }
119
120 # Partition can't have two partition styles
121 if ($null -ne $gpt_type -and $null -ne $mbr_type) {
122 $module.FailJson("Cannot specify both GPT and MBR partition styles. Check which partition style is supported by the disk")
123 }
124
New-AnsiblePartition()125 function New-AnsiblePartition {
126 param(
127 $DiskNumber,
128 $Letter,
129 $SizeMax,
130 $Size,
131 $MbrType,
132 $GptType,
133 $Style
134 )
135
136 $parameters = @{
137 DiskNumber = $DiskNumber
138 }
139
140 if ($null -ne $Letter) {
141 switch ($Letter) {
142 "auto" {
143 $parameters.Add("AssignDriveLetter", $True)
144 }
145 default {
146 $parameters.Add("DriveLetter", $Letter)
147 }
148 }
149 }
150
151 if ($null -ne $Size) {
152 $parameters.Add("Size", $Size)
153 }
154
155 if ($null -ne $MbrType) {
156 $parameters.Add("MbrType", $Style)
157 }
158
159 if ($null -ne $GptType) {
160 $parameters.Add("GptType", $Style)
161 }
162
163 try {
164 $new_partition = New-Partition @parameters
165 } catch {
166 $module.FailJson("Unable to create a new partition: $($_.Exception.Message)", $_)
167 }
168
169 return $new_partition
170 }
171
172
Set-AnsiblePartitionState()173 function Set-AnsiblePartitionState {
174 param(
175 $hidden,
176 $read_only,
177 $active,
178 $partition
179 )
180
181 $parameters = @{
182 DiskNumber = $partition.DiskNumber
183 PartitionNumber = $partition.PartitionNumber
184 }
185
186 if ($hidden -NotIn ($null, $partition.IsHidden)) {
187 $parameters.Add("IsHidden", $hidden)
188 }
189
190 if ($read_only -NotIn ($null, $partition.IsReadOnly)) {
191 $parameters.Add("IsReadOnly", $read_only)
192 }
193
194 if ($active -NotIn ($null, $partition.IsActive)) {
195 $parameters.Add("IsActive", $active)
196 }
197
198 try {
199 Set-Partition @parameters
200 } catch {
201 $module.FailJson("Error changing state of partition: $($_.Exception.Message)", $_)
202 }
203 }
204
205
206 if ($ansible_partition) {
207 if ($state -eq "absent") {
208 try {
209 Remove-Partition -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber -Confirm:$false -WhatIf:$module.CheckMode
210 } catch {
211 $module.FailJson("There was an error removing the partition: $($_.Exception.Message)", $_)
212 }
213 $module.Result.changed = $true
214 }
215 else {
216
217 if ($null -ne $gpt_type -and $gpt_styles.$gpt_type -ne $ansible_partition.GptType) {
218 $module.FailJson("gpt_type is not a valid parameter for existing partitions")
219 }
220 if ($null -ne $mbr_type -and $mbr_styles.$mbr_type -ne $ansible_partition.MbrType) {
221 $module.FailJson("mbr_type is not a valid parameter for existing partitions")
222 }
223
224 if ($partition_size) {
225 try {
226 $max_supported_size = (Get-PartitionSupportedSize -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber).SizeMax
227 } catch {
228 $module.FailJson("Unable to get maximum supported partition size: $($_.Exception.Message)", $_)
229 }
230 if ($size_is_maximum) {
231 $ansible_partition_size = $max_supported_size
232 }
233 if ($ansible_partition_size -ne $ansible_partition.Size -and ($ansible_partition_size - $ansible_partition.Size -gt 1049000 -or $ansible_partition.Size - $ansible_partition_size -gt 1049000)) {
234 if ($ansible_partition.IsReadOnly) {
235 $module.FailJson("Unable to resize partition: Partition is read only")
236 } else {
237 try {
238 Resize-Partition -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber -Size $ansible_partition_size -WhatIf:$module.CheckMode
239 } catch {
240 $module.FailJson("Unable to change partition size: $($_.Exception.Message)", $_)
241 }
242 $module.Result.changed = $true
243 }
244 } elseif ($ansible_partition_size -gt $max_supported_size) {
245 $module.FailJson("Specified partition size exceeds size supported by the partition")
246 }
247 }
248
249 if ($drive_letter -NotIn ("auto", $null, $ansible_partition.DriveLetter)) {
250 if (-not $module.CheckMode) {
251 try {
252 Set-Partition -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber -NewDriveLetter $drive_letter
253 } catch {
254 $module.FailJson("Unable to change drive letter: $($_.Exception.Message)", $_)
255 }
256 }
257 $module.Result.changed = $true
258 }
259 }
260 }
261 else {
262 if ($state -eq "present") {
263 if ($null -eq $disk_number) {
264 $module.FailJson("Missing required parameter: disk_number")
265 }
266 if ($null -eq $ansible_partition_size -and -not $size_is_maximum){
267 $module.FailJson("Missing required parameter: partition_size")
268 }
269 if (-not $size_is_maximum) {
270 try {
271 $max_supported_size = (Get-Disk -Number $disk_number).LargestFreeExtent
272 } catch {
273 $module.FailJson("Unable to get maximum size supported by disk: $($_.Exception.Message)", $_)
274 }
275
276 if ($ansible_partition_size -gt $max_supported_size) {
277 $module.FailJson("Partition size is not supported by disk. Use partition_size: -1 to get maximum size")
278 }
279 } else {
280 $ansible_partition_size = (Get-Disk -Number $disk_number).LargestFreeExtent
281 }
282
283 $supp_part_type = (Get-Disk -Number $disk_number).PartitionStyle
284 if ($null -ne $mbr_type) {
285 if ($supp_part_type -eq "MBR" -and $mbr_styles.ContainsKey($mbr_type)) {
286 $partition_style = $mbr_styles.$mbr_type
287 } else {
288 $module.FailJson("Incorrect partition style specified")
289 }
290 }
291 if ($null -ne $gpt_type) {
292 if ($supp_part_type -eq "GPT" -and $gpt_styles.ContainsKey($gpt_type)) {
293 $partition_style = $gpt_styles.$gpt_type
294 } else {
295 $module.FailJson("Incorrect partition style specified")
296 }
297 }
298
299 if (-not $module.CheckMode) {
300 $ansible_partition = New-AnsiblePartition -DiskNumber $disk_number -Letter $drive_letter -Size $ansible_partition_size -MbrType $mbr_type -GptType $gpt_type -Style $partition_style
301 }
302 $module.Result.changed = $true
303 }
304 }
305
306 if ($state -eq "present" -and $ansible_partition) {
307 if ($offline -NotIn ($null, $ansible_partition.IsOffline)) {
308 if (-not $module.CheckMode) {
309 try {
310 Set-Partition -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber -IsOffline $offline
311 } catch {
312 $module.FailJson("Error setting partition offline: $($_.Exception.Message)", $_)
313 }
314 }
315 $module.Result.changed = $true
316 }
317
318 if ($hidden -NotIn ($null, $ansible_partition.IsHidden) -or $read_only -NotIn ($null, $ansible_partition.IsReadOnly) -or $active -NotIn ($null, $ansible_partition.IsActive)) {
319 if (-not $module.CheckMode) {
320 Set-AnsiblePartitionState -hidden $hidden -read_only $read_only -active $active -partition $ansible_partition
321 }
322 $module.Result.changed = $true
323 }
324 }
325
326 $module.ExitJson()
327