1 #!powershell
2 
3 # Copyright: (c) 2019, 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         drive_letter = @{ type = "str" }
16         path = @{ type = "str" }
17         label = @{ type = "str" }
18         new_label = @{ type = "str" }
19         file_system = @{ type = "str"; choices = "ntfs", "refs", "exfat", "fat32", "fat" }
20         allocation_unit_size = @{ type = "int" }
21         large_frs = @{ type = "bool" }
22         full = @{ type = "bool"; default = $false }
23         compress = @{ type = "bool" }
24         integrity_streams = @{ type = "bool" }
25         force = @{ type = "bool"; default = $false }
26     }
27     mutually_exclusive = @(
28         ,@('drive_letter', 'path', 'label')
29     )
30     required_one_of = @(
31         ,@('drive_letter', 'path', 'label')
32     )
33     supports_check_mode = $true
34 }
35 
36 $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
37 
38 $drive_letter = $module.Params.drive_letter
39 $path = $module.Params.path
40 $label = $module.Params.label
41 $new_label = $module.Params.new_label
42 $file_system = $module.Params.file_system
43 $allocation_unit_size = $module.Params.allocation_unit_size
44 $large_frs = $module.Params.large_frs
45 $full_format = $module.Params.full
46 $compress_volume = $module.Params.compress
47 $integrity_streams = $module.Params.integrity_streams
48 $force_format = $module.Params.force
49 
50 # Some pre-checks
51 if ($null -ne $drive_letter -and $drive_letter -notmatch "^[a-zA-Z]$") {
52     $module.FailJson("The parameter drive_letter should be a single character A-Z")
53 }
54 if ($integrity_streams -eq $true -and $file_system -ne "refs") {
55     $module.FailJson("Integrity streams can be enabled only on ReFS volumes. You specified: $($file_system)")
56 }
57 if ($compress_volume -eq $true) {
58     if ($file_system -eq "ntfs") {
59         if ($null -ne $allocation_unit_size -and $allocation_unit_size -gt 4096) {
60             $module.FailJson("NTFS compression is not supported for allocation unit sizes above 4096")
61         }
62     }
63     else {
64         $module.FailJson("Compression can be enabled only on NTFS volumes. You specified: $($file_system)")
65     }
66 }
67 
Get-AnsibleVolumenull68 function Get-AnsibleVolume {
69     param(
70         $DriveLetter,
71         $Path,
72         $Label
73     )
74 
75     if ($null -ne $DriveLetter) {
76         try {
77             $volume = Get-Volume -DriveLetter $DriveLetter
78         } catch {
79             $module.FailJson("There was an error retrieving the volume using drive_letter $($DriveLetter): $($_.Exception.Message)", $_)
80         }
81     }
82     elseif ($null -ne $Path) {
83         try {
84             $volume = Get-Volume -Path $Path
85         } catch {
86             $module.FailJson("There was an error retrieving the volume using path $($Path): $($_.Exception.Message)", $_)
87         }
88     }
89     elseif ($null -ne $Label) {
90         try {
91             $volume = Get-Volume -FileSystemLabel $Label
92         } catch {
93             $module.FailJson("There was an error retrieving the volume using label $($Label): $($_.Exception.Message)", $_)
94         }
95     }
96     else {
97         $module.FailJson("Unable to locate volume: drive_letter, path and label were not specified")
98     }
99 
100     return $volume
101 }
102 
Format-AnsibleVolume()103 function Format-AnsibleVolume {
104     param(
105         $Path,
106         $Label,
107         $FileSystem,
108         $Full,
109         $UseLargeFRS,
110         $Compress,
111         $SetIntegrityStreams
112     )
113     $parameters = @{
114         Path = $Path
115         Full = $Full
116     }
117     if ($null -ne $UseLargeFRS) {
118         $parameters.Add("UseLargeFRS", $UseLargeFRS)
119     }
120     if ($null -ne $SetIntegrityStreams) {
121         $parameters.Add("SetIntegrityStreams", $SetIntegrityStreams)
122     }
123     if ($null -ne $Compress){
124         $parameters.Add("Compress", $Compress)
125     }
126     if ($null -ne $Label) {
127         $parameters.Add("NewFileSystemLabel", $Label)
128     }
129     if ($null -ne $FileSystem) {
130         $parameters.Add("FileSystem", $FileSystem)
131     }
132 
133     Format-Volume @parameters -Confirm:$false | Out-Null
134 
135 }
136 
137 $ansible_volume = Get-AnsibleVolume -DriveLetter $drive_letter -Path $path -Label $label
138 $ansible_file_system = $ansible_volume.FileSystem
139 $ansible_volume_size = $ansible_volume.Size
140 
141 $ansible_partition = Get-Partition -Volume $ansible_volume
142 
143 foreach ($access_path in $ansible_partition.AccessPaths) {
144     if ($access_path -ne $Path) {
145         $files_in_volume = (Get-ChildItem -LiteralPath $access_path -ErrorAction SilentlyContinue | Measure-Object).Count
146 
147         if (-not $force_format -and $files_in_volume -gt 0) {
148             $module.FailJson("Force format must be specified to format non-pristine volumes")
149         } else {
150             if (-not $force_format -and
151                 -not $null -eq $file_system -and
152                 -not [string]::IsNullOrEmpty($ansible_file_system) -and
153                 $file_system -ne $ansible_file_system) {
154                 $module.FailJson("Force format must be specified since target file system: $($file_system) is different from the current file system of the volume: $($ansible_file_system.ToLower())")
155             } else {
156                 $pristine = $true
157             }
158         }
159     }
160 }
161 
162 if ($force_format) {
163     if (-not $module.CheckMode) {
164         Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume
165     }
166     $module.Result.changed = $true
167 }
168 else {
169     if ($pristine) {
170         if ($null -eq $new_label) {
171             $new_label = $ansible_volume.FileSystemLabel
172         }
173         # Conditions for formatting
174         if ($ansible_volume_size -eq 0 -or
175             $ansible_volume.FileSystemLabel -ne $new_label) {
176             if (-not $module.CheckMode) {
177                 Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume
178             }
179             $module.Result.changed = $true
180         }
181     }
182 }
183 
184 $module.ExitJson()
185