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         $AllocationUnitSize
113     )
114     $parameters = @{
115         Path = $Path
116         Full = $Full
117     }
118     if ($null -ne $UseLargeFRS) {
119         $parameters.Add("UseLargeFRS", $UseLargeFRS)
120     }
121     if ($null -ne $SetIntegrityStreams) {
122         $parameters.Add("SetIntegrityStreams", $SetIntegrityStreams)
123     }
124     if ($null -ne $Compress){
125         $parameters.Add("Compress", $Compress)
126     }
127     if ($null -ne $Label) {
128         $parameters.Add("NewFileSystemLabel", $Label)
129     }
130     if ($null -ne $FileSystem) {
131         $parameters.Add("FileSystem", $FileSystem)
132     }
133     if ($null -ne $AllocationUnitSize) {
134         $parameters.Add("AllocationUnitSize", $AllocationUnitSize)
135     }
136 
137     Format-Volume @parameters -Confirm:$false | Out-Null
138 
139 }
140 
141 $ansible_volume = Get-AnsibleVolume -DriveLetter $drive_letter -Path $path -Label $label
142 $ansible_file_system = $ansible_volume.FileSystem
143 $ansible_volume_size = $ansible_volume.Size
144 $ansible_volume_alu = (Get-CimInstance -ClassName Win32_Volume -Filter "DeviceId = '$($ansible_volume.path.replace('\','\\'))'" -Property BlockSize).BlockSize
145 
146 $ansible_partition = Get-Partition -Volume $ansible_volume
147 
148 if (-not $force_format -and $null -ne $allocation_unit_size -and $ansible_volume_alu -ne 0 -and $null -ne $ansible_volume_alu -and $allocation_unit_size -ne $ansible_volume_alu) {
149         $module.FailJson("Force format must be specified since target allocation unit size: $($allocation_unit_size) is different from the current allocation unit size of the volume: $($ansible_volume_alu)")
150 }
151 
152 foreach ($access_path in $ansible_partition.AccessPaths) {
153     if ($access_path -ne $Path) {
154         if ($null -ne $file_system -and
155             -not [string]::IsNullOrEmpty($ansible_file_system) -and
156             $file_system -ne $ansible_file_system)
157         {
158             if (-not $force_format)
159             {
160                 $no_files_in_volume = (Get-ChildItem -LiteralPath $access_path -ErrorAction SilentlyContinue | Measure-Object).Count -eq 0
161                 if($no_files_in_volume)
162                 {
163                     $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())")
164                 }
165                 else
166                 {
167                     $module.FailJson("Force format must be specified to format non-pristine volumes")
168                 }
169             }
170         }
171         else
172         {
173             $pristine = -not $force_format
174         }
175     }
176 }
177 
178 if ($force_format) {
179     if (-not $module.CheckMode) {
180         Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume -AllocationUnitSize $allocation_unit_size
181     }
182     $module.Result.changed = $true
183 }
184 else {
185     if ($pristine) {
186         if ($null -eq $new_label) {
187             $new_label = $ansible_volume.FileSystemLabel
188         }
189         # Conditions for formatting
190         if ($ansible_volume_size -eq 0 -or
191             $ansible_volume.FileSystemLabel -ne $new_label) {
192             if (-not $module.CheckMode) {
193                 Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume -AllocationUnitSize $allocation_unit_size
194             }
195             $module.Result.changed = $true
196         }
197     }
198 }
199 
200 $module.ExitJson()
201