CoolFunctions.psm1


<#
    .SYNOPSIS
        Resize an image using the specified scaling factor
    .PARAMETER Source
        The image to be resized
    .PARAMETER Destination
        The output path for the resized image
    .PARAMETER Scale
        The scaling factor to apply
#>

Function Resize-Image
{
    [CmdletBinding()]
    [OutputType([void])]
    param
    (
        [Parameter(ValueFromPipeline=$true,Mandatory=$true,ParameterSetName='io')]
        [System.IO.FileInfo[]]$Source,        
        [Parameter()]
        [System.String]$Destination = $env:TEMP,
        [Parameter()]
        [System.Double]$Scale = 0.50
    )
    BEGIN
    {
        $ScaleTransform=New-Object System.Windows.Media.ScaleTransform($Scale,$scale)
    }
    PROCESS
    {
        foreach($item in $Source)
        {
            #Prevent the file from getting locked
            $ImageSource=New-Object System.Windows.Media.Imaging.BitmapImage
            $ImageSource.BeginInit()
            $ImageSource.UriSource=$item.FullName
            $ImageSource.CacheOption=[System.Windows.Media.Imaging.BitmapCacheOption]::OnLoad
            $ImageSource.EndInit()
            ## Open and resize the image
            $image = New-Object System.Windows.Media.Imaging.TransformedBitmap ($ImageSource,$ScaleTransform)
            ## Put it on the clipboard (just for fun)
            [System.Windows.Clipboard]::SetImage($image)

            $destinationPath=Join-Path $Destination $item.Name
            Write-Verbose "Creating $destinationPath"
            ## Write out an image file:
            $stream = [System.IO.File]::Open($destinationPath, "OpenOrCreate")
            $encoder = New-Object System.Windows.Media.Imaging.$($item.Extension.Substring(1))BitmapEncoder
            $encoder.Frames.Add([System.Windows.Media.Imaging.BitmapFrame]::Create($image))
            $encoder.Save($stream)
            $stream.Dispose()
        }
    }
    END
    {

    }
}

<#
    .SYNOPSIS
        Compresses the contents of the specified folder to a Zip file
    .PARAMETER FileName
        The path to the file to be created
    .PARAMETER SourcePath
        The path of the content to compress
    .PARAMETER File
        The file to be created
    .PARAMETER Source
        The source of the content
    .PARAMETER Overwrite
        Overwrite an existing file
    .PARAMETER IncludeParent
        Include the specified folder within the archive
    .PARAMETER OptimalCompression
        Use optimal compression
    .PARAMETER FastestCompression
        Use fastest compression
#>

function Compress-ZipFileFromFolder
{
    [CmdletBinding(DefaultParameterSetName='string')]
    param
    (
        [Parameter(Position=0,Mandatory=$true,ParameterSetName='string')]
        [System.String]$FileName,
        [Parameter(Position=1,Mandatory=$true,ParameterSetName='string')]
        [System.String]$SourcePath,
        [Parameter(Position=0,Mandatory=$true,ParameterSetName='io')]
        [System.IO.FileInfo]$File,
        [Parameter(Position=1,Mandatory=$true,ParameterSetName='io')]
        [System.IO.FileSystemInfo]$Source,
        [Parameter(Position=2,ParameterSetName='string')]
        [Parameter(Position=2,ParameterSetName='io')]
        [Switch]$OverWrite,
        [Parameter(Position=3,ParameterSetName='string')]
        [Parameter(Position=3,ParameterSetName='io')]
        [bool]$IncludeParent=$true,
        [Parameter(Position=4,ParameterSetName='string')]
        [Parameter(Position=4,ParameterSetName='io')]
        [bool]$OptimalCompression=$true,
        [Parameter(Position=5,ParameterSetName='string')]
        [Parameter(Position=5,ParameterSetName='io')]
        [Switch]$FastestCompression
    )
    $Parent=$false
    if($IncludeParent)
    {
        Write-Debug "New-ZipFileFromFolder - IncludeParent Switch Specified"
        $Parent=$true
    }
    if($PSCmdlet.ParameterSetName -eq 'io')
    {
        $FileName=$File.FullName
        $SourcePath=$Source.FullName
    }
    if($OverWrite)
    {
        Write-Debug "New-ZipFileFromFolder - Overwrite Switch Specified"
        if(Test-Path -Path $FileName)
        {
            Write-Warning "New-ZipFileFromFolder - Deleting $FileName"
            Remove-Item -Path $FileName -Force
        }
    }

   if($OptimalCompression)
   {
        Write-Debug "New-ZipFileFromFolder - Overwrite Switch Specified"
        $CompressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
   }
   [System.IO.Compression.ZipFile]::CreateFromDirectory($SourcePath,$FileName, $CompressionLevel, $Parent)
   Write-Verbose "New-ZipFileFromFolder - Created Zip File $FileName"
}

<#
    .SYNOPSIS
        Downloads a file from a URL to the specified location
    .PARAMETER Source
        The uri of the item to download
    .PARAMETER DownloadPath
        The path of the downloaded file
#>

Function Copy-WebFile
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [System.Uri]$Source,
        [Parameter()]
        [System.String]$DownloadPath=(Join-Path $env:USERPROFILE "Downloads")

    )

    $FileName=Split-Path $Source.AbsolutePath -Leaf
    $Destination=Join-Path $DownloadPath $FileName
    $webclient = New-Object System.Net.WebClient
    $webclient.DownloadFile($Source,$Destination)

}

<#
    .SYNOPSIS
        Formats and indents an XmlDocument to a string
    .PARAMETER Xml
        An XmlDocument to be formatted
    .PARAMETER XmlString
        A valid string of an XML document
         
#>

Function Format-XML
{
    [CmdletBinding(DefaultParameterSetName="dom")]
    Param
    (
        [Parameter(Position=0,ValueFromPipeLine=$true,Mandatory=$true,ParameterSetName="dom")]
        [System.Xml.XmlDocument]$Xml,
        [Parameter(Position=0,ValueFromPipeLine=$true,Mandatory=$true,ParameterSetName="string")]
        [System.String]$XmlString
    )
    if($PSCmdlet.ParameterSetName -eq "string")
    {
        $Xml=New-Object System.Xml.XmlDocument
        $Xml.LoadXml($XmlString)
    }
    $sw=New-Object System.IO.StringWriter
    $writer=New-Object System.Xml.XmlTextWriter($sw)
    $writer.Formatting = [System.Xml.Formatting]::Indented
    $Xml.WriteContentTo($writer)
    return $sw.ToString()
}

<#
    .SYNOPSIS
        Converts a CIDR or Prefix Length as string to a Subnet Mask
    .PARAMETER CIDR
        The CIDR or Prefix Length to convert
 
#>

Function ConvertTo-SubnetMaskFromCIDR
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String]
        $CIDR
    )
    $NetMask="0.0.0.0"
    $CIDRLength=[System.Convert]::ToInt32(($CIDR.Split('/')|Select-Object -Last 1).Trim())
    Write-Debug "Converting Prefix Length $CIDRLength from input $CIDR"
    switch ($CIDRLength) {
        {$_ -gt 0 -and $_ -lt 8}
        {
            $binary="$( "1" * $CIDRLength)".PadRight(8,"0")
            $o1 = [System.Convert]::ToInt32($binary.Trim(),2)       
            $NetMask = "$o1.0.0.0"
            break
        }
        8 {$NetMask="255.0.0.0"}
        {$_ -gt 8 -and $_ -lt 16}
        {
            $binary="$( "1" * ($CIDRLength - 8))".PadRight(8,"0")
            $o2 = [System.Convert]::ToInt32($binary.Trim(),2)       
            $NetMask = "255.$o2.0.0"
            break
        }
        16 {$NetMask="255.255.0.0"}
        {$_ -gt 16 -and $_ -lt 24}
        {
            $binary="$("1" * ($CIDRLength - 16))".PadRight(8,"0")
            $o3 = [System.Convert]::ToInt32($binary.Trim(),2)
            $NetMask = "255.255.$o3.0"
            break
        }
        24 {$NetMask="255.255.255.0"}
        {$_ -gt 24 -and $_ -lt 32}
        {
            $binary="$("1" * ($CIDRLength - 24))".PadRight(8,"0")
            $o4 = [convert]::ToInt32($binary.Trim(),2)              
            $NetMask= "255.255.255.$o4"
            break
        }
        32 {$NetMask="255.255.255.255"}
    }
    return $NetMask
}

<#
    .SYNOPSIS
        Converts a CIDR as string to a network address
    .PARAMETER CIDR
        The CIDR to convert
#>

Function ConvertTo-NetworkAddressFromCIDR
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String]
        $CIDR
    )
    
    $IpString=$CIDR.Split("/")[0]
    $PrefixLength=[System.Convert]::ToInt32($CIDR.Split("/")[1])
    [System.Net.IPAddress]$IpAddress=$null
    if([System.Net.IPAddress]::TryParse($IpString,[ref]$IpAddress))
    {
        $AddressBytes=$IpAddress.GetAddressBytes()
        [Array]::Reverse($AddressBytes)
        $IpAsUint=[System.BitConverter]::ToInt32($AddressBytes,0)
        #Bitwise AND the NetMask and the IP
        $NetMask=[Convert]::ToUInt32(("1" * $PrefixLength).PadRight(32, "0"), 2)
        $NetAddressBytes=[BitConverter]::GetBytes(($IpAsUint -band $NetMask))
        [System.Array]::Reverse($NetAddressBytes)
        $NetAddressAsIp=New-Object System.Net.IPAddress -ArgumentList (,$NetAddressBytes)
        return $NetAddressAsIp.IPAddressToString
    }
    else
    {
        throw "Unable to parse the IP Address $IpString"
    }
}

<#
    .SYNOPSIS
        Converts a CIDR as string to a broadcast address
    .PARAMETER CIDR
        The CIDR to convert
#>

Function ConvertTo-BroadcastAddressFromCIDR
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String]
        $CIDR
    )
    
    $IpString=$CIDR.Split("/")[0]
    $PrefixLength=[System.Convert]::ToInt32($CIDR.Split("/")[1])
    [System.Net.IPAddress]$IpAddress=$null
    if([System.Net.IPAddress]::TryParse($IpString,[ref]$IpAddress))
    {
        $AddressBytes=$IpAddress.GetAddressBytes()
        [Array]::Reverse($AddressBytes)
        $IpAsUint=[System.BitConverter]::ToInt32($AddressBytes,0)
        #Bitwise OR the Host Mask and the IP
        $HostMask = [Convert]::ToUInt32("1" * (32 - $PrefixLength), 2)
        $NetAddressBytes=[BitConverter]::GetBytes(($IpAsUint -bor $HostMask))
        [System.Array]::Reverse($NetAddressBytes)
        $NetAddressAsIp=New-Object System.Net.IPAddress -ArgumentList (,$NetAddressBytes)
        return $NetAddressAsIp.IPAddressToString
    }
    else
    {
        throw "Unable to parse the IP Address $IpString"
    }
}

<#
    .SYNOPSIS
        Converts a CIDR as string to the last address
    .PARAMETER CIDR
        The CIDR to convert
#>

Function ConvertTo-NetworkRangeEndFromCIDR
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String]
        $CIDR
    )
    
    $IpString=$CIDR.Split("/")[0]
    $PrefixLength=[System.Convert]::ToInt32($CIDR.Split("/")[1])
    [System.Net.IPAddress]$IpAddress=$null
    if([System.Net.IPAddress]::TryParse($IpString,[ref]$IpAddress))
    {
        $AddressBytes=$IpAddress.GetAddressBytes()
        [Array]::Reverse($AddressBytes)
        $IpAsUint=[System.BitConverter]::ToInt32($AddressBytes,0)
        #Bitwise OR the Host Mask and the IP
        $HostMask = [Convert]::ToUInt32("1" * (32 - $PrefixLength), 2)
        $LastAddress=($IpAsUint -bor $HostMask)-1
        $NetAddressBytes=[BitConverter]::GetBytes($LastAddress)
        [System.Array]::Reverse($NetAddressBytes)
        $NetAddressAsIp=New-Object System.Net.IPAddress -ArgumentList (,$NetAddressBytes)
        return $NetAddressAsIp.IPAddressToString
    }
    else
    {
        throw "Unable to parse the IP Address $IpString"
    }
}

<#
    .SYNOPSIS
        Converts a Subnet Mask to a Prefix Length
    .PARAMETER SubnetMask
        The SubnetMask to convert
#>

Function ConvertTo-PrefixLengthFromSubnetMask
{
    [CmdletBinding()]
    [OutputType([Int32])]
    param
    (
        # Subnet Mask
        [ValidatePattern("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")]
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [string]
        $SubnetMask
    )

    [System.Net.IPAddress]$SubnetAsIp=$null
    if([System.Net.IPAddress]::TryParse($SubnetMask,[ref]$SubnetAsIp))
    {
        $MaskString=[String]::Empty
        foreach ($AddressByte in $SubnetAsIp.GetAddressBytes())
        {
            $MaskString+=[Convert]::ToString($AddressByte,2)    
        }
        $MaskString=$MaskString -Replace '[\s0]'
        return $MaskString.Length
    }
    else 
    {
        throw "Unable to parse the Subnet Mask $SubnetMask!"    
    }
}

<#
    .SYNOPSIS
        Copies a file with a progress stream
    .PARAMETER From
        The file to be copied
    .PARAMETER To
        The destination file
    .PARAMETER ToDirectory
        The destination directory
    .PARAMETER ActivityName
        The Progress ActivityName
    .PARAMETER ActivityId
        The Progress Activity Id
    .PARAMETER ParentActivityId
        The Parent Activity
 
#>

Function Copy-FileWithProgress 
{
    [CmdletBinding(DefaultParameterSetName='fileinfo')]
    param
    (
        [Parameter(Mandatory=$true,ParameterSetName='fileinfo',ValueFromPipeline=$true)]
        [Parameter(Mandatory=$true,ParameterSetName='dirinfo',ValueFromPipeline=$true)]
        [System.IO.FileInfo]
        $From,
        [Parameter(Mandatory=$true,ParameterSetName='fileinfo')]
        [System.IO.FileInfo]
        $To,
        [Parameter(Mandatory=$true,ParameterSetName='dirinfo')]
        [System.IO.DirectoryInfo]
        $ToDirectory,        
        [Parameter(Mandatory=$false,ParameterSetName='dirinfo')]
        [Parameter(Mandatory=$false,ParameterSetName='fileinfo')]
        [System.String]
        $ActivityName="Copying file",
        [Parameter(Mandatory=$false,ParameterSetName='fileinfo')]
        [Parameter(Mandatory=$false,ParameterSetName='dirinfo')]
        [System.Int32]
        $ActivityId=80085,
        [Parameter(Mandatory=$false,ParameterSetName='dirinfo')]
        [Parameter(Mandatory=$false,ParameterSetName='fileinfo')]
        [System.Int32]
        $ParentActivityId=0,
        [Parameter(Mandatory=$false,ParameterSetName='dirinfo')]
        [Parameter(Mandatory=$false,ParameterSetName='fileinfo')]
        [uint32]$BufferLength=1MB
    )

  if($PSCmdlet.ParameterSetName -eq 'dirinfo')
    {
        if(-not $ToDirectory.Exists)
        {
            New-Item -Path $ToDirectory.Parent.FullName -Name $ToDirectory.Name -ItemType Directory -Force|Out-Null
        }
        $To=New-Object System.IO.FileInfo((Join-Path $ToDirectory.FullName $From.Name))
    }

    $ffile = $From.OpenRead()
    $Tofile = $To.OpenWrite()
    $CurrentProgress=0
    $FileSizeInMb=$From.Length/1MB
    $StopWatch=[System.Diagnostics.Stopwatch]::StartNew()

    Write-Verbose "BEGIN:Copying $From -> $To ..."
    Write-Progress -Id $ActivityId -Activity $ActivityName `
        -ParentId $ParentActivityId `
        -Status "Copying $From -> $To" `
        -PercentComplete $CurrentProgress
    try 
    {
        [System.Byte[]]$FileBuffer = New-Object System.Byte[] $BufferLength
        [long]$Total = [long]$ReadCount = 0
        [long]$TotalCopiedInMb=0
        do 
        {
            $ReadCount = $ffile.Read($FileBuffer, 0, $FileBuffer.Length)
            $Tofile.Write($FileBuffer, 0, $ReadCount)
            $Total += $ReadCount
            $TotalCopiedInMb=$Total/1MB
            $CurrentSpeed=($TotalCopiedInMb/$StopWatch.Elapsed.TotalSeconds).ToString("#.##")
            if ($Total % 1mb -eq 0)
            {
                $CurrentProgress=[int]($TotalCopiedInMb/$FileSizeInMb * 100)
                Write-Progress -Id $ActivityId `
                -Activity "$ActivityName %$CurrentProgress"`
                -ParentId $ParentActivityId `
                -Status "Copying $From -> $To ($($TotalCopiedInMb.ToString("#.##")) of $($FileSizeInMb.ToString("#.##")) Mb) $($CurrentSpeed) MB/s" `
                -PercentComplete $CurrentProgress
            }
        } while ($ReadCount -gt 0)
        Write-Progress -Id $ActivityId -Activity $ActivityName `
            -ParentId $ParentActivityId `
            -Status "Copying $From -> $To" `
            -PercentComplete $CurrentProgress -Completed
        $StopWatch.Stop()
        Write-Verbose "END:Copy $From -> $To Took:$($StopWatch.ElapsedMilliseconds)ms. $($CurrentSpeed) MB/s"
    }
    catch [System.IO.IOException],[System.Exception]
    {
        Write-Warning "Error Copying File $_"
    }
    finally
    {
        $ffile.Dispose()
        $Tofile.Dispose()
        if($StopWatch -ne $null -and $StopWatch.IsRunning)
        {
            $StopWatch.Stop()
        }
    }
}

<#
    .SYNOPSIS
        Converts a Unix Timestamp to DateTime
    .PARAMETER UnixTime
        The Unix Timestamp to be converted
#>

Function ConvertFrom-UnixTime
{
    [OutputType([System.DateTime])]
    param
    (
        [Parameter(Mandatory=$true)]
        [double]
        $UnixTime
    )
    $epoch = New-Object System.DateTime(1970, 1, 1, 0, 0, 0, 0)
    return $epoch.AddSeconds($UnixTime)
}

<#
    .SYNOPSIS
        Converts a DateTime to a Unix Timestamp
    .PARAMETER DateTime
        The DateTime to be converted
#>

Function ConvertTo-UnixTime
{
    [OutputType([System.Double])]
    param
    (
        [Parameter(Mandatory=$true)]
        [datetime]
        $DateTime
    )
    $epoch = New-Object System.DateTime(1970, 1, 1, 0, 0, 0, 0);
    $delta = $DateTime - $epoch;
    return [Math]::Floor($delta.TotalSeconds);
}

<#
    .SYNOPSIS
    Converts a DateTime to ISO 5601 Time string
#>

Function ConvertTo-Iso8601Time
{
    [OutputType([String])]
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.DateTime]
        $Time
    )

    $Offset=New-Object System.DateTimeOffset($Time)
    return $Offset.ToString('o')
}

<#
    .SYNOPSIS
        Simple constructor wrapper for PSCredential
    .PARAMETER UserName
        The UserName
    .PARAMETER Password
        The Password as a SecureString
    .PARAMETER ClearPassword
        The Password as plain text
#>

Function New-PSCredential
{
    [OutputType([SecureString])]
    [CmdletBinding(DefaultParameterSetName='Secure')]
    param
    (
        [Parameter(Mandatory=$true,ParameterSetName='Secure')]
        [Parameter(Mandatory=$true,ParameterSetName='Plain')]
        [string]
        $UserName,
        [Parameter(Mandatory=$true,ParameterSetName='Secure')]
        [securestring]
        $Password,
        [Parameter(Mandatory=$true,ParameterSetName='Plain')]
        [string]
        $ClearPassword
    )

    if($PSCmdlet.ParameterSetName -eq 'Plain')
    {
        $Password=ConvertTo-SecureString -String $ClearPassword -AsPlainText -Force
    }
    $Credential=New-Object PSCredential($UserName,$Password)
    return $Credential
}