DSCResources/MSFT_xExchAutoMountPoint/MSFT_xExchAutoMountPoint.psm1

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagDatabasesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagVolumesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String[]]
        $DiskToDBMap,

        [parameter(Mandatory = $true)]
        [System.UInt32]
        $SpareVolumeCount,

        [ValidateSet("NTFS","REFS")]
        [System.String]
        $FileSystem = "NTFS",

        [System.String]
        $MinDiskSize = "",

        [ValidateSet("MBR","GPT")]
        [System.String]
        $PartitioningScheme = "GPT",

        [System.String]
        $UnitSize = "64K",

        [System.String]
        $VolumePrefix = "EXVOL"
    )

    #Load helper module
    Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)").Parent.Parent.FullName)\Misc\xExchangeCommon.psm1" -Verbose:0

    LogFunctionEntry -VerbosePreference $VerbosePreference

    GetDiskInfo

    $dbMap = GetDiskToDBMap -AutoDagDatabasesRootFolderPath $AutoDagDatabasesRootFolderPath

    $returnValue = @{
        Identity = $Identity
        DiskToDBMap = $dbMap
        SpareVolumeCount = $SpareVolumeCount
        AutoDagDatabasesRootFolderPath = $AutoDagDatabasesRootFolderPath
        AutoDagVolumesRootFolderPath = $AutoDagVolumesRootFolderPath
        VolumePrefix = $VolumePrefix
        MinDiskSize = $MinDiskSize
        UnitSize = $UnitSize
        PartitioningScheme = $PartitioningScheme
        FileSystem = $FileSystem
    }

    $returnValue
}

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagDatabasesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagVolumesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String[]]
        $DiskToDBMap,

        [parameter(Mandatory = $true)]
        [System.UInt32]
        $SpareVolumeCount,

        [ValidateSet("NTFS","REFS")]
        [System.String]
        $FileSystem = "NTFS",

        [System.String]
        $MinDiskSize = "",

        [ValidateSet("MBR","GPT")]
        [System.String]
        $PartitioningScheme = "GPT",

        [System.String]
        $UnitSize = "64K",

        [System.String]
        $VolumePrefix = "EXVOL"
    )

    #Load helper module
    Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)").Parent.Parent.FullName)\Misc\xExchangeCommon.psm1" -Verbose:0

    LogFunctionEntry -VerbosePreference $VerbosePreference

    #First see if we need to assign any disks to ExVol's
    GetDiskInfo
    
    $exVolCount = GetInUseMountPointCount -RootFolder $AutoDagVolumesRootFolderPath
    $requiredVolCount = $DiskToDBMap.Count + $SpareVolumeCount
   
    if ($exVolCount -lt $requiredVolCount)
    {
        CreateMissingExVolumes @PSBoundParameters -CurrentVolCount $exVolCount -RequiredVolCount $requiredVolCount
    }

    #Now see if we need any DB mount points
    GetDiskInfo

    $exDbCount = GetInUseMountPointCount -RootFolder $AutoDagDatabasesRootFolderPath
    $requiredDbCount = GetDesiredDatabaseCount -DiskToDBMap $DiskToDBMap
    
    if ($exDbCount -lt $requiredDbCount)
    {
        CreateMissingExDatabases @PSBoundParameters
    }
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagDatabasesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagVolumesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String[]]
        $DiskToDBMap,

        [parameter(Mandatory = $true)]
        [System.UInt32]
        $SpareVolumeCount,

        [ValidateSet("NTFS","REFS")]
        [System.String]
        $FileSystem = "NTFS",

        [System.String]
        $MinDiskSize = "",

        [ValidateSet("MBR","GPT")]
        [System.String]
        $PartitioningScheme = "GPT",

        [System.String]
        $UnitSize = "64K",

        [System.String]
        $VolumePrefix = "EXVOL"
    )

    #Load helper module
    Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)").Parent.Parent.FullName)\Misc\xExchangeCommon.psm1" -Verbose:0

    LogFunctionEntry -VerbosePreference $VerbosePreference

    GetDiskInfo

    #Check if the number of assigned EXVOL's is less than the requested number of DB disks plus spares
    $mountPointCount = GetInUseMountPointCount -RootFolder $AutoDagVolumesRootFolderPath

    if ($mountPointCount -lt ($DiskToDBMap.Count + $SpareVolumeCount))
    {
        ReportBadSetting -SettingName "MountPointCount" -ExpectedValue ($DiskToDBMap.Count + $SpareVolumeCount) -ActualValue $mountPointCount -VerbosePreference $VerbosePreference
        return $false
    }
    else #Loop through all requested DB's and see if they have a mount point yet
    {
        foreach ($value in $DiskToDBMap)
        {
            foreach ($db in $value.Split(','))
            {
                if ((DBHasMountPoint -AutoDagDatabasesRootFolderPath $AutoDagDatabasesRootFolderPath -DB $db) -eq $false)
                {
                    ReportBadSetting -SettingName "DB '$($db)' Has Mount Point" -ExpectedValue $true -ActualValue $false -VerbosePreference $VerbosePreference
                    return $false
                }
            }
        }
    }

    return $true
}

#Creates mount points for any Exchange Volumes we are missing
function CreateMissingExVolumes
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagDatabasesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagVolumesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String[]]
        $DiskToDBMap,

        [parameter(Mandatory = $true)]
        [System.UInt32]
        $SpareVolumeCount,

        [ValidateSet("NTFS","REFS")]
        [System.String]
        $FileSystem = "NTFS",

        [System.String]
        $MinDiskSize = "",

        [ValidateSet("MBR","GPT")]
        [System.String]
        $PartitioningScheme = "GPT",

        [System.String]
        $UnitSize = "64K",

        [System.String]
        $VolumePrefix = "EXVOL",

        [System.Int32]
        $CurrentVolCount,

        [System.Int32]
        $RequiredVolCount
    )

    for ($i = $CurrentVolCount; $i -lt $RequiredVolCount; $i++)
    {
        if ($i -ne $CurrentVolCount) #Need to update disk info if we've gone through the loop already
        {
            GetDiskInfo
        }

        $firstDisk = FindFirstAvailableDisk -MinDiskSize $MinDiskSize

        if ($firstDisk -ne -1)
        {
            $firstVolume = FindFirstAvailableVolumeNumber -AutoDagVolumesRootFolderPath $AutoDagVolumesRootFolderPath -VolumePrefix $VolumePrefix

            if ($firstVolume -ne -1)
            {
                $volPath = Join-Path -Path "$($AutoDagVolumesRootFolderPath)" -ChildPath "$($VolumePrefix)$($firstVolume)"

                PrepareVolume -DiskNumber $firstDisk -Folder $volPath -FileSystem $FileSystem -UnitSize $UnitSize -PartitioningScheme $PartitioningScheme -Label "$($VolumePrefix)$($firstVolume)"
            }
            else
            {
                throw "Unable to find a free volume number to use when naming the volume folder"
            }
        }
        else
        {
            throw "No available disks to assign an Exchange Volume mount point to"
        }
    }
}

#Looks for databases that have never had a mount point created, and gives them a mount point
function CreateMissingExDatabases
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagDatabasesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String]
        $AutoDagVolumesRootFolderPath,

        [parameter(Mandatory = $true)]
        [System.String[]]
        $DiskToDBMap,

        [parameter(Mandatory = $true)]
        [System.UInt32]
        $SpareVolumeCount,

        [ValidateSet("NTFS","REFS")]
        [System.String]
        $FileSystem = "NTFS",

        [System.String]
        $MinDiskSize = "",

        [ValidateSet("MBR","GPT")]
        [System.String]
        $PartitioningScheme = "GPT",

        [System.String]
        $UnitSize = "64K",

        [System.String]
        $VolumePrefix = "EXVOL"
    )

    for ($i = 0; $i -lt $DiskToDBMap.Count; $i++)
    {
        if ($i -gt 0) #Need to refresh current disk info
        {
            GetDiskInfo
        }

        [string[]]$dbsNeedingMountPoints = @()

        [string[]]$allDBsRequestedForDisk = $DiskToDBMap[$i].Split(',')

        for ($j = 0; $j -lt $allDBsRequestedForDisk.Count; $j++)
        {
            $current = $allDBsRequestedForDisk[$j]

            $path = Join-Path -Path "$($AutoDagDatabasesRootFolderPath)" -ChildPath "$($current)"

            #We only want to touch datases who have never had a mount point created. After that, AutoReseed will handle it.
            if ((Test-Path -Path "$($path)") -eq $false)
            {
                $dbsNeedingMountPoints += $current
            }
            else #Since the folder already exists, need to check and error if the mount point doesn't
            {
                if ((MountPointExists -Path $path) -eq $false)
                {
                    throw "Database '$($current)' already has a folder on disk at '$($path)', but does not have a mount point. This must be manually corrected for xAutoMountPoint to proceed."
                }
            }
        }

        if ($dbsNeedingMountPoints.Count -eq $allDBsRequestedForDisk.Count) #No DB mount points for this disk have been created yet
        {
            $targetVolume = GetExchangeVolume -AutoDagDatabasesRootFolderPath $AutoDagDatabasesRootFolderPath -AutoDagVolumesRootFolderPath $AutoDagVolumesRootFolderPath -DBsPerDisk $allDBsRequestedForDisk.Count
        }
        elseif ($dbsNeedingMountPoints.Count -gt 0) #We just need to create some mount points
        {
            $existingDB = ""

            #Find a DB that's already had its mount point created
            foreach ($db in $allDBsRequestedForDisk)
            {
                if (($dbsNeedingMountPoints.Contains($db) -eq $false))
                {
                    $existingDB = $db
                    break
                }
            }

            if ($existingDB -ne "")
            {
                $targetVolume = GetExchangeVolume -AutoDagDatabasesRootFolderPath $AutoDagDatabasesRootFolderPath -AutoDagVolumesRootFolderPath $AutoDagVolumesRootFolderPath -ExistingDB $existingDB -DBsPerDisk $allDBsRequestedForDisk.Count -DBsToCreate $dbsNeedingMountPoints.Count
            }
        }
        else #All DB's requested for this disk are good. Just continue on in the loop
        {
            continue
        }

        if ($targetVolume -ne $null)
        {
            if ($targetVolume -ne -1)
            {
                foreach ($db in $dbsNeedingMountPoints)
                {
                    $path = Join-Path -Path "$($AutoDagDatabasesRootFolderPath)" -ChildPath "$($db)"

                    AddMountPoint -VolumeNumber $targetVolume -Folder $path
                }
            }
            else
            {
                throw "Unable to find a volume to place mount points for the following databases: '$($dbsNeedingMountPoints)'"
            }
        }
    }
}

#Builds a map of the DBs that already exist on disk
function GetDiskToDBMap
{
    param([string]$AutoDagDatabasesRootFolderPath)

    #Get the DB path to a point where we know there will be a trailing \
    $dbpath = Join-Path -Path "$($AutoDagDatabasesRootFolderPath)" -ChildPath ""

    #Keep track of a disk number for putting in the map
    $i = 0

    #Will be the return value for DiskToDBMap
    [string[]]$dbMap = @()

    #Loop through all existing mount points and figure out which ones are for DB's
    foreach ($key in $global:VolumeToMountPointMap.Keys)
    {
        [string]$mountPoints = ""

        foreach ($mountPoint in $global:VolumeToMountPointMap[$key])
        {
            if ($mountPoint.StartsWith($dbpath))
            {
                $startIndex = $dbpath.Length
                $endIndex = $mountPoint.IndexOf("\", $startIndex)
                $dbName = $mountPoint.Substring($startIndex, $endIndex - $startIndex)

                if ($mountPoints -eq "")
                {
                    $mountPoints = $dbName
                }
                else
                {
                    $mountPoints += ",$($dbName)"
                }
            }
        }

        if ($mountPoints.Length -gt 0)
        {
            $dbMap += $mountPoints
        }
    }

    return $dbMap
}

#Looks for a volume where an Exchange Volume or Database mount point can be added.
#If ExistingDB is not specified, looks for a spare volume that has no mount points yet.
#If ExistingDB is specified, finds the volume number where that DB exists, only if there is room to
#create the requested database mount points.
function GetExchangeVolume
{
    param([string]$AutoDagDatabasesRootFolderPath, [string]$AutoDagVolumesRootFolderPath, [string]$ExistingDB = "", [Uint32]$DBsPerDisk, [Uint32]$DBsToCreate)

    $targetVol = -1 #Our return variable

    $keysSorted = $global:VolumeToMountPointMap.Keys | Sort-Object
    
    #Loop through every volume
    foreach ($key in $keysSorted)
    {
        #Get mount points for this volume
        [string[]]$mountPoints = $global:VolumeToMountPointMap[$key]

        $hasExVol = $false #Whether any ExVol mount points exist on this disk
        $hasExDb = $false #Whether any ExDB mount points exist on this disk
        $hasExistingDB = $false #Whether $ExistingDB exists as a mount point on this disk

        #Inspect each individual mount point
        foreach($mountPoint in $mountPoints)
        {
            if ($mountPoint.StartsWith($AutoDagVolumesRootFolderPath))
            {
                $hasExVol = $true
            }
            elseif ($mountPoint.StartsWith($AutoDagDatabasesRootFolderPath))
            {
                $hasExDb = $true

                $path = Join-Path -Path "$($AutoDagDatabasesRootFolderPath)" -ChildPath "$($ExistingDB)"

                if ($mountPoint.StartsWith($path))
                {
                    $hasExistingDB = $true
                }
            }
        }

        if ($ExistingDB -eq "")
        {
            if ($hasExVol -eq $true -and $hasExDb -eq $false)
            {
                $targetVol = $key
                break
            }
        }
        else
        {
            if ($hasExVol -eq $true -and $hasExistingDB -eq $true)
            {
                if (($mountPoints.Count + $DBsToCreate) -le ($DBsPerDisk + 1))
                {
                    $targetVol = $key
                }

                break
            }
        }
    }

    return $targetVol
}

#Finds the lowest disk number that doesn't have any volumes associated, and is larger than the requested size
function FindFirstAvailableDisk
{
    param([string]$MinDiskSize = "")

    $diskNum = -1

    foreach ($key in $global:DiskToVolumeMap.Keys)
    {
        if ($global:DiskToVolumeMap[$key].Count -eq 0 -and ($key -lt $diskNum -or $diskNum -eq -1))
        {
            if ($MinDiskSize -ne "")
            {
                [Uint64]$minSize = 0 + $MinDiskSize.Replace(" ", "")
                [Uint64]$actualSize = 0 + $global:DiskSizeMap[$key].Replace(" ", "")

                if ($actualSize -gt $minSize)
                {
                    $diskNum = $key
                }
            }
            else
            {
                $diskNum = $key
            }
        }
    }

    return $diskNum
}

#Looks in the volumes root folder and finds the first number we can give to a volume folder
#based off of what folders have already been created
function FindFirstAvailableVolumeNumber
{
    param([string]$AutoDagVolumesRootFolderPath, [string]$VolumePrefix)

    if((Test-Path -LiteralPath "$($AutoDagVolumesRootFolderPath)") -eq $false) #If the ExVol folder doesn't already exist, then we can start with 1
    {
        return 1
    }

    $currentFolders = Get-ChildItem -LiteralPath "$($AutoDagVolumesRootFolderPath)" | where {$_.GetType().Name -eq "DirectoryInfo"} | Sort-Object

    for ($i = 1; $i -lt 999; $i++)
    {
        $existing = $null
        $existing = $currentFolders | where {$_.Name -eq "$($VolumePrefix)$($i)"}

        if ($existing -eq $null)
        {
            return $i
        }
    }

    return -1
}

#Counts and returns the number of DB's in the disk to db map
function GetDesiredDatabaseCount
{
    param([string[]]$DiskToDBMap)

    $count = 0

    foreach ($value in $DiskToDBMap)
    {
        $count += $value.Split(',').Count
    }

    return $count
}


#Checks if a database already has a mountpoint created
function DBHasMountPoint
{
    param([string]$AutoDagDatabasesRootFolderPath, [string]$DB)

    $dbPath = Join-Path -Path "$($AutoDagDatabasesRootFolderPath)" -ChildPath "$($DB)"

    foreach ($key in $global:VolumeToMountPointMap.Keys)
    {
        foreach ($mountPoint in $global:VolumeToMountPointMap[$key])
        {
            if ($mountPoint.StartsWith($dbPath))
            {
                return $true
            }
        }
    }

    return $false
}

#Gets the count of in use mount points matching the given critera
function GetInUseMountPointCount
{
    param([string]$RootFolder)

    $count = 0

    foreach ($key in $global:VolumeToMountPointMap.Keys)
    {
        foreach ($mountPoint in $global:VolumeToMountPointMap[$key])
        {
            if ($mountPoint.StartsWith($RootFolder))
            {
                $count++
            }
        }
    }

    return $count
}

#Checks whether the mount point specified in the given path already exists as a mount point
function MountPointExists
{
    param([string]$Path)

    foreach ($key in $global:VolumeToMountPointMap.Keys)
    {
        foreach ($value in $global:VolumeToMountPointMap[$key])
        {
            #Make sure both paths end with the same character
            if (($value.EndsWith("\")) -eq $false)
            {
                $value += "\"
            }

            if (($Path.EndsWith("\")) -eq $false)
            {
                $Path += "\"
            }

            #Do the comparison
            if ($value -like $Path)
            {
                return $true
            }
        }
    }

    return $false
}

#Adds the array of commands to a single temp file, and has disk part execute the temp file
function Run-Diskpart
{
    [CmdletBinding()]
    [OutputType([System.String])]
    Param ([Array]$Commands, [Boolean]$ShowOutput = $true)

    $Tempfile = [System.IO.Path]::GetTempFileName()

    foreach ($Com in $Commands)
    {
        $CMDLine = $CMDLine + $Com + ", "
        Add-Content $Tempfile $Com
    }

    $Output = DiskPart /s $Tempfile

    if ($ShowOutput)
    {
        Write-Verbose "Executed Diskpart commands: $(StringArrayToCommaSeparatedString -Array $Commands). Result:"
        Write-Verbose "$($Output)"
    }

    Remove-Item $Tempfile

    return $Output
}

function StringArrayToCommaSeparatedString
{
    param([string[]]$Array)

    $string = ""

    if ($Array -ne $null -and $Array.Count -gt 0)
    {
        $string = $Array[0]

        for ($i = 1; $i -lt $Array.Count; $i++)
        {
            $string += ",$($Array[$i])"
        }
    }

    return $string
}

#Uses diskpart to obtain information on the disks and volumes that already exist on the system
function GetDiskInfo
{
    [Hashtable]$global:DiskToVolumeMap = @{}
    [Hashtable]$global:VolumeToMountPointMap = @{}
    [Hashtable]$global:DiskSizeMap = @{}
    [int[]]$diskNums = @()

    $diskList = Run-Diskpart -Commands "List Disk" -ShowOutput $false 

    $foundDisks = $false

    #First parse out the list of disks
    foreach ($line in $diskList)
    {
        if ($foundDisks -eq $true)
        {
            if ($line.Contains("Disk "))
            {
                #First find the disk number
                $startIndex = " Disk ".Length
                $endIndex = " -------- ".Length
                $diskNumStr = $line.Substring($startIndex, $endIndex - $startIndex).Trim()

                if ($diskNumStr.Length -gt 0)
                {
                    $diskNum = [int]::Parse($diskNumStr)
                    $diskNums += $diskNum
                }

                #Now find the disk size
                $startIndex = " -------- ------------- ".Length
                $endIndex = " -------- ------------- ------- ".Length
                $diskSize = $line.Substring($startIndex, $endIndex - $startIndex).Trim()

                if ($diskSize.Length -gt 0 -and $diskNum -ne $null)
                {
                    $DiskSizeMap.Add($diskNum, $diskSize)
                }
            }
        }
        elseif ($line.Contains("-------- ------------- ------- ------- --- ---")) #Scroll forward until we find the where the list of disks starts
        {
            $foundDisks = $true
        }
    }

    #Now get info on the disks
    foreach ($diskNum in $diskNums)
    {
        $diskDetails = Run-Diskpart -Commands "Select Disk $($diskNum)","Detail Disk" -ShowOutput $false

        $foundVolumes = $false

        for ($i = 0; $i -lt $diskDetails.Count; $i++)
        {
            $line = $diskDetails[$i]

            if ($foundVolumes -eq $true)
            {
                if ($line.StartsWith(" Volume "))
                {
                    #First find the volume number
                    $volStart = " Volume ".Length
                    $volEnd = " ---------- ".Length
                    $volStr = $line.Substring($volStart, $volEnd - $volStart).Trim()

                    if ($volStr.Length -gt 0)
                    {
                        $volNum = [int]::Parse($volStr)

                        AddObjectToMapOfObjectArrays -Map $DiskToVolumeMap -Key $diskNum -Value $volNum

                        #Now parse out the drive letter if it's set
                        $letterStart = " ---------- ".Length
                        $letterEnd = $line.IndexOf(" ---------- --- ") + " ---------- --- ".Length
                        $letter = $line.Substring($letterStart, $letterEnd - $letterStart).Trim()

                        if ($letter.Length -eq 1)
                        {
                            AddObjectToMapOfObjectArrays -Map $VolumeToMountPointMap -Key $volNum -Value $letter
                        }

                        #Now find all the mount points
                        do
                        {
                            $line = $diskDetails[++$i]

                            if ($line -eq $null -or $line.StartsWith(" Volume ") -or $line.Trim().Length -eq 0) #We've hit the next volume, or the end of all info
                            {
                                $i-- #Move $i back one as we may have overrun the start of the next volume info
                                break
                            }
                            else
                            {
                                $mountPoint = $line.Trim()

                                AddObjectToMapOfObjectArrays -Map $VolumeToMountPointMap -Key $volNum -Value $mountPoint
                            }

                        } while ($i -lt $diskDetails.Count)

                    }
                }
            }
            elseif ($line.Contains("There are no volumes."))
            {
                [string[]]$emptyArray = @()
                $DiskToVolumeMap[$diskNum] = $emptyArray

                break
            }
            elseif ($line.Contains("---------- --- ----------- ----- ---------- ------- --------- --------"))
            {
                $foundVolumes = $true
            }
        }
    }
}

#Takes an empty disk, initalizes and formats it, and gives it an ExchangeVolume mount point
function PrepareVolume
{
    param([int]$DiskNumber, [string]$Folder, [string]$FileSystem, [string]$UnitSize, [string]$PartitioningScheme, [string]$Label)

    $formatString = "Format FS=$($FileSystem) UNIT=$($UnitSize) Label=$($Label) QUICK"

    #Initialize the disk and put in MBR format
    Run-Diskpart -Commands "select disk $($DiskNumber)","clean" | Out-Null
    Run-Diskpart -Commands "select disk $($DiskNumber)","online disk" | Out-Null
    Run-Diskpart -Commands "select disk $($DiskNumber)","attributes disk clear readonly","convert MBR" | Out-Null
    Run-Diskpart -Commands "select disk $($DiskNumber)","offline disk" | Out-Null
 
    #Online the disk
    Run-Diskpart -Commands "select disk $($DiskNumber)","attributes disk clear readonly","online disk" | Out-Null

    #Convert to GPT if requested
    if ($PartitioningScheme -eq "GPT")
    {
        Run-Diskpart -Commands "select disk $($DiskNumber)","convert GPT noerr" | Out-Null
    }

    #Create the directory if it doesn't exist
    if ((Test-Path $Folder) -eq $False)
    {
        mkdir -Path "$($Folder)" | Out-Null
    }    

    #Create the partition and format the drive
    Run-Diskpart -Commands "select disk $($DiskNumber)","create partition primary","$($formatString)","assign mount=`"$($Folder)`"" | Out-Null
}

#Adds a mount point to an existing volume
function AddMountPoint
{
    param([int]$VolumeNumber, [string]$Folder)

    #Create the directory if it doesn't exist
    if ((Test-Path $Folder) -eq $False)
    {
        mkdir -Path "$($Folder)" | Out-Null
    }

    Run-Diskpart -Commands "select volume $($VolumeNumber)","assign mount=`"$($Folder)`"" | Out-Null
}

#Takes a hashtable, and adds the given key and value.
function AddObjectToMapOfObjectArrays
{
    Param([Hashtable]$Map, $Key, $Value)

    if ($Map.ContainsKey($Key))
    {
        $Map[$Key] += $Value
    }
    else
    {
        [object[]]$Array = $Value
        $Map[$Key] = $Array
    }
}


Export-ModuleMember -Function *-TargetResource