FileSystemHelper.psm1

function Get-FileSystemObjectSecurity
{
    [CmdletBinding()]
    [OutputType([NtfsPermissionsHelper.PermissionCollection])]
    param
    (
        # Path
        [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [string]$Path
    )

    Begin
    {
        $FunctionName = $MyInvocation.MyCommand.Name
    }

    Process
    {
        $errPref = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        try
        {
            [NtfsPermissionsHelper.PermissionCollection]::GetSecurity($Path)
        }
        catch
        {
            Write-Error -Exception "$_" -Message "$_" -Category NotSpecified -ErrorId 0 -TargetObject $FunctionName -ErrorAction $errPref
        }
    }

    End
    {

    }
}

function Set-FileSystemObjectSecurity
{
    [CmdletBinding(DefaultParameterSetName="NoRemoting_ByPermissionList")]
    [OutputType([NtfsPermissionsHelper.PermissionCollection])]
    param
    (
        # Path
        [Parameter(Mandatory=$true)]
        [string]$Path,

        # PermissionCollection
        [Parameter(Mandatory=$true, ParameterSetName="NoRemoting_ByPermissionsObject")]
        [NtfsPermissionsHelper.PermissionCollection]$PermissionCollection,

        # ExplicitPermissions
        [Parameter(Mandatory=$false, ParameterSetName="NoRemoting_ByPermissionList")]
        [NtfsPermissionsHelper.PermissionEntry[]]$ExplicitPermissions,

        # BreakInheritance
        [Parameter(Mandatory=$false, ParameterSetName="NoRemoting_ByPermissionList")]
        [switch]$BreakInheritance,

        # PreserveInheritedRules
        [Parameter(Mandatory=$false, ParameterSetName="NoRemoting_ByPermissionList")]
        [switch]$PreserveInheritedRules,

        # PassThru
        [Parameter(Mandatory=$false)]
        [switch]$PassThru
    )

    Begin
    {
        $FunctionName = $MyInvocation.MyCommand.Name
    }

    Process
    {
        $errPref = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        try
        {
            switch($PSCmdlet.ParameterSetName)
            {
                "NoRemoting_ByPermissionsObject"
                {
                    $PermissionCollection.SetPermissions($Path)
                }

                "NoRemoting_ByPermissionList"
                {
                    $SecurityObj = [NtfsPermissionsHelper.PermissionCollection]::CreateNew()

                    if($BreakInheritance.IsPresent)
                    {
                        $SecurityObj.InheritanceIsBroken = $true

                        if($PreserveInheritedRules.IsPresent)
                        {
                            $CurrentSecurity = [NtfsPermissionsHelper.PermissionCollection]::GetSecurity($Path)
                            $SecurityObj.ExplicitPermissions.AddRange($CurrentSecurity.InheritedPermissions)
                        }
                    }

                    if($ExplicitPermissions)
                    {
                        $SecurityObj.ExplicitPermissions.AddRange($ExplicitPermissions)
                    }

                    $SecurityObj.SetPermissions($Path)
                }
            }

            if($PassThru.IsPresent)
            {
                [NtfsPermissionsHelper.PermissionCollection]::GetSecurity($Path)
            }
        }
        catch
        {
            Write-Error -Exception "$_" -Message "$_" -Category NotSpecified -ErrorId 0 -TargetObject $FunctionName -ErrorAction $errPref
        }
    }

    End
    {

    }
}

function Set-FileSystemObjectOwner
{
    [CmdletBinding()]
    [OutputType([NtfsPermissionsHelper.PermissionCollection])]
    param
    (
        # Path
        [Parameter(Position=0, Mandatory=$true)]
        [string]$Path,

        # Identity
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByIdentity')]
        [ValidateNotNull()]
        [string]$Identity,

        # IdentityReference
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByIdentityReference')]
        [ValidateNotNull()]
        [System.Security.Principal.IdentityReference]$IdentityReference,

        # DomainName
        [Parameter(Mandatory=$false, ParameterSetName='NoRemoting_ByUserName')]
        [AllowNull()]
        [AllowEmptyString()]
        [string]$DomainName,

        # UserName
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByUserName')]
        [ValidateNotNullOrEmpty()]
        [string]$UserName,

        # PassThru
        [Parameter(Mandatory=$false)]
        [switch]$PassThru
    )

    Begin
    {
        $FunctionName = $MyInvocation.MyCommand.Name
    }

    Process
    {
        $errPref = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        try
        {
            if($($PSCmdlet.ParameterSetName) -like "NoRemoting_ByIdentity")
            {
                $AceAccount = [AccountManagement.IdentityInfo]::Resolve($Identity)
            }
            elseif($($PSCmdlet.ParameterSetName) -like "NoRemoting_ByIdentityReference")
            {
                $AceAccount = [AccountManagement.IdentityInfo]::Resolve($IdentityReference)
            }
            elseif($($PSCmdlet.ParameterSetName) -like "NoRemoting_ByUserName")
            {
                $AceAccount = [AccountManagement.IdentityInfo]::Resolve($DomainName,$UserName)
            }

            $SecurityObj = [NtfsPermissionsHelper.PermissionCollection]::GetSecurity($Path)
            $SecurityObj.Owner = $AceAccount

        # [NtfsPermissionsHelper.TokenPrivilegeHelper]::EnablePrivilege([NtfsPermissionsHelper.PrivilegeNames]::SeBackupPrivilege)
            $HasPrivilege = [NtfsPermissionsHelper.TokenPrivilegeHelper]::EnablePrivilege([NtfsPermissionsHelper.PrivilegeNames]::SeRestorePrivilege)

            $SecurityObj.SetOwner($Path)

            if($PassThru.IsPresent)
            {
                [NtfsPermissionsHelper.PermissionCollection]::GetSecurity($Path)
            }
        }
        catch
        {
            Write-Error -Exception "$_" -Message "$_" -Category NotSpecified -ErrorId 0 -TargetObject $FunctionName -ErrorAction $errPref
        }
    }

    End
    {

    }
}

function New-PermissionEntry
{
    [CmdletBinding()]
    [OutputType([NtfsPermissionsHelper.PermissionEntry])]
    param
    (
        # Identity
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByIdentityAndCommonRights')]
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByIdentityAndSpecialRights')]
        [ValidateNotNull()]
        [string]$Identity,

        # IdentityReference
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByIdentityReferenceAndCommonRights')]
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByIdentityReferenceAndSpecialRights')]
        [ValidateNotNull()]
        [System.Security.Principal.IdentityReference]$IdentityReference,

        # DomainName
        [Parameter(Mandatory=$false, ParameterSetName='NoRemoting_ByUserNameAndCommonRights')]
        [Parameter(Mandatory=$false, ParameterSetName='NoRemoting_ByUserNameAndSpecialRights')]
        [AllowNull()]
        [AllowEmptyString()]
        [string]$DomainName,

        # UserName
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByUserNameAndCommonRights')]
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByUserNameAndSpecialRights')]
        [ValidateNotNullOrEmpty()]
        [string]$UserName,

        # AppliesTo (This parameter is ignored for files. It is valid only for folders.)
        [Parameter(Mandatory=$false)]
        [NtfsPermissionsHelper.AppliesToOptions]$AppliesTo = [NtfsPermissionsHelper.AppliesToOptions]::ThisFolderSubfoldersAndFiles,

        # CommonRights
        [Parameter(Mandatory=$false, ParameterSetName='NoRemoting_ByIdentityAndCommonRights')]
        [Parameter(Mandatory=$false, ParameterSetName='NoRemoting_ByIdentityReferenceAndSpecialRights')]
        [Parameter(Mandatory=$false, ParameterSetName='NoRemoting_ByUserNameAndCommonRights')]
        [NtfsPermissionsHelper.CommonRights]$CommonRights = [NtfsPermissionsHelper.CommonRights]::FullControl,

        # SpecialRights
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByIdentityAndSpecialRights')]
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByIdentityReferenceAndSpecialRights')]
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByUserNameAndSpecialRights')]
        [ValidateSet('AppendData', 'ChangePermissions', 'CreateDirectories', 'CreateFiles', 'Delete', 'DeleteSubdirectoriesAndFiles', 'ExecuteFile', 'FullControl', 'ListDirectory', 'Modify', 'Read', 'ReadAndExecute', 'ReadAttributes', 'ReadData', 'ReadExtendedAttributes', 'ReadPermissions', 'TakeOwnership', 'Traverse', 'Write', 'WriteAttributes', 'WriteData', 'WriteExtendedAttributes')]
        [string[]]$SpecialRights,

        # AccessType
        [Parameter(Mandatory=$false)]
        [NtfsPermissionsHelper.AccessTypeOptions]$AccessType = [NtfsPermissionsHelper.AccessTypeOptions]::Allow,

        # WellKnownACE
        [Parameter(Mandatory=$true, ParameterSetName='NoRemoting_ByWellKnownACE')]
        [ValidateSet('LocalAdministrators','CreatorOwner','LocalSystem')]
        [string[]]$WellKnownACE
    )

    Begin
    {
        $FunctionName = $MyInvocation.MyCommand.Name
    }

    Process
    {
        $errPref = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        try
        {
            if($($PSCmdlet.ParameterSetName) -like "NoRemoting_ByWellKnownACE")
            {
                if($WellKnownACE -contains 'LocalAdministrators')
                {
                    $AceAccount = [AccountManagement.IdentityInfo]::Resolve('BUILTIN','Administrators')
                    [NtfsPermissionsHelper.PermissionEntry]::CreateNew($AceAccount)
                }

                if($WellKnownACE -contains 'LocalSystem')
                {
                    $AceAccount = [AccountManagement.IdentityInfo]::Resolve('NT AUTHORITY','SYSTEM')
                    [NtfsPermissionsHelper.PermissionEntry]::CreateNew($AceAccount)
                }

                if($WellKnownACE -contains 'CreatorOwner')
                {
                    $AceAccount = [AccountManagement.IdentityInfo]::Resolve($null,'CREATOR OWNER')
                    [NtfsPermissionsHelper.PermissionEntry]::CreateNew($AceAccount,[NtfsPermissionsHelper.CommonRights]::FullControl,[NtfsPermissionsHelper.AppliesToOptions]::SubfoldersAndFilesOnly)
                }
            }
            else
            {
                if($($PSCmdlet.ParameterSetName) -like "NoRemoting_ByIdentityAnd*")
                {
                    $AceAccount = [AccountManagement.IdentityInfo]::Resolve($Identity)
                }
                elseif($($PSCmdlet.ParameterSetName) -like "NoRemoting_ByIdentityReferenceAnd*")
                {
                    $AceAccount = [AccountManagement.IdentityInfo]::Resolve($IdentityReference)
                }
                elseif($($PSCmdlet.ParameterSetName) -like "NoRemoting_ByUserNameAnd*")
                {
                    $AceAccount = [AccountManagement.IdentityInfo]::Resolve($DomainName,$UserName)
                }

                $entryObj = [NtfsPermissionsHelper.PermissionEntry]::CreateNew($AceAccount,$CommonRights,$AppliesTo,$AccessType)

                if($($PSCmdlet.ParameterSetName) -like "*SpecialRights")
                {
                    $SpecialRightsFlags = $($SpecialRights | ForEach-Object -Begin { $flags = 0; } -Process { $flags = $($flags -bor $([System.Security.AccessControl.FileSystemRights]$_)); } -End { [System.Security.AccessControl.FileSystemRights]$flags })
                    $entryObj.FileSystemRights = $SpecialRightsFlags
                }

                $entryObj
            }
        }
        catch
        {
            Write-Error -Exception "$_" -Message "$_" -Category NotSpecified -ErrorId 0 -TargetObject $FunctionName -ErrorAction $errPref
        }
    }

    End
    {

    }
}

function Add-FileSystemObjectPermissionEntry
{
    [CmdletBinding()]
    [OutputType([NtfsPermissionsHelper.PermissionCollection])]
    param
    (
        # Path
        [Parameter(Mandatory=$true)]
        [string]$Path,

        # Identity
        [Parameter(Mandatory=$true)]
        [ValidateNotNull()]
        [NtfsPermissionsHelper.PermissionEntry]$PermissionEntry,

        # Force
        [Parameter(Mandatory=$false)]
        [switch]$Force,

        # PassThru
        [Parameter(Mandatory=$false)]
        [switch]$PassThru
    )

    Begin
    {
        $FunctionName = $MyInvocation.MyCommand.Name
    }

    Process
    {
        $errPref = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        try
        {
            $Permissions = [NtfsPermissionsHelper.PermissionCollection]::GetSecurity($Path)

            if($($PermissionEntry.MatchesItemInCollection($Permissions.ExplicitPermissions)) -and ( -not $($Force.IsPresent)))
            {
                Write-Verbose "The specified Permission Entry is already configured on object $($Path):$PermissionEntry"
            }
            else
            {
                Write-Verbose "Adding PermissionEntry on object $($Path):$PermissionEntry"
                $Permissions.ExplicitPermissions.Add($PermissionEntry)
                $Permissions.SetPermissions($Path)
            }

            if($PassThru.IsPresent)
            {
                [NtfsPermissionsHelper.PermissionCollection]::GetSecurity($Path)
            }
        }
        catch
        {
            Write-Error -Exception "$_" -Message "$_" -Category NotSpecified -ErrorId 0 -TargetObject $FunctionName -ErrorAction $errPref
        }
    }

    End
    {

    }
}

function Wait-FileSystemItemLock
{

    [CmdletBinding()]
    param
    (
        #FilePath
        [Parameter(Mandatory=$true)]
        [System.IO.FileInfo]$FilePath,

        #WaitTimeoutInSeconds
        [Parameter(Mandatory=$false)]
        [int]$WaitTimeoutInSeconds = 300,

        #WaitIntervalInSeconds
        [Parameter(Mandatory=$false)]
        [int]$WaitIntervalInSeconds = 2
    )

    process
    {
        $Timer = [System.Diagnostics.Stopwatch]::StartNew()

        while (!$FileIsUnlocked)
        {
            #Check File
            try
            {
                $FileOpen = $FilePath.Open([System.IO.FileMode]::Truncate)
                $FileIsUnlocked = $true
            }
            catch
            {
                $FileIsUnlocked = $false
            }

            #Check Timeout
            if ($Timer.Elapsed.TotalSeconds -ge $WaitTimeoutInSeconds)
            {
                throw "Item: $($FilePath.FullName) is still Locked, timeout of $WaitTimeoutInSeconds reaced."
            }

            #Sleep
            if (!$FileIsUnlocked)
            {
                Start-Sleep -Seconds $WaitIntervalInSeconds
            }
            
        }


    }

}

function Find-FileSharedPath
{
    [CmdletBinding()]
    [OutputType([void])]
    param
    (
        #FilePath
        [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')]
        [string]$FilePath,

        #ReplaceMachineFQDN
        [Parameter(Mandatory=$false,ParameterSetName='NoRemoting_Default')]
        [string]$ReplaceMachineFQDN
    )
    
    Process
    {
        if ($PSBoundParameters.ContainsKey('ReplaceMachineFQDN'))
        {
            $ComputerFqdn = $ReplaceMachineFQDN
        }
        else
        {
            $DomainFQDN= (Get-WmiObject Win32_ComputerSystem).Domain
            $ComputerFqdn = "$env:COMPUTERNAME.$DomainFQDN"
        }

        $allShares = Get-SmbShare -Special:$false
        $MatchingShares = $allShares | Where-Object {$FilePath.StartsWith($_.Path,[System.StringComparison]::OrdinalIgnoreCase)} | Sort-Object -Property Name | select -First 1 -ErrorAction Stop
        if ($MatchingShares)
        {
            $FilePath.Replace($MatchingShares.path,"\\$ComputerFqdn\$($MatchingShares.Name)")
        }
        else
        {
            throw "No Shared paths to: $FilePath"
        }
    }
    
}