Skip to main content

Create a symbolic link to a file or directory in PowerShell.

Function New-SymLink
{
    <#
        .SYNOPSIS
            Creates a Symbolic link to a file or directory.

        .DESCRIPTION
            Creates a Symbolic link to a file or directory as an alternative to mklink.exe.

        .PARAMETER Path
            Name of the path that you will reference with a symbolic link.

        .PARAMETER SymName
            Name of the symbolic link to create. Can be a full path/unc or just the name.
            If only a name is given, the symbolic link will be created on the current directory that the
            function is being run on.

        .PARAMETER File
            Create a file symbolic link.

        .PARAMETER Directory
            Create a directory symbolic link

        .EXAMPLE
            PS > New-SymLink -Path "C:\users\admin\downloads" -SymName "C:\users\admin\desktop\downloads" -Directory

            SymLink                          Target                   Type
            -------                          ------                   ----
            C:\Users\admin\Desktop\Downloads C:\Users\admin\Downloads Directory

            Creates a symbolic link to downloads folder that resides on C:\users\admin\desktop.

        .EXAMPLE
            PS > New-SymLink -Path "C:\users\admin\downloads\document.txt" -SymName "SomeDocument" -File

            SymLink                             Target                                Type
            -------                             ------                                ----
            C:\users\admin\desktop\SomeDocument C:\users\admin\downloads\document.txt File

            Creates a symbolic link to document.txt file under the current directory called SomeDocument.

        .LINK
            https://github.com/proxb/PowerShell_Scripts/blob/master/New-SymLink.ps1

        .NOTES
            Name: New-SymLink
            Author: Boe Prox
            Created: 15 Jul 2013
    #>
    [CmdletBinding(DefaultParameterSetName = 'Directory', SupportsShouldProcess = $True)]
    param (
        [Parameter(Position = 0, ParameterSetName = 'Directory', ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, Mandatory = $True)]
        [Parameter(Position = 0, ParameterSetName = 'File', ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, Mandatory = $True)]
        [ValidateScript({
                If (Test-Path $_) {$True} Else
                {
                    Throw "`'$_`' doesn't exist!"
                }
            })]
        [string]
        $Path,

        [Parameter(Position = 1, ParameterSetName = 'Directory')]
        [Parameter(Position = 1, ParameterSetName = 'File')]
        [string]
        $SymName,

        [Parameter(Position = 2, ParameterSetName = 'File')]
        [switch]
        $File,

        [Parameter(Position = 2, ParameterSetName = 'Directory')]
        [switch]
        $Directory
    )
    begin
    {
        try
        {
            $null = [mklink.symlink]
        }
        catch
        {
            Add-Type @'
            using System;
            using System.Runtime.InteropServices;

            namespace mklink
            {
                public class symlink
                {
                    [DllImport("kernel32.dll")]
                    public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
                }
            }
'@
        }
    }

    process
    {
        # Assume target symlink is on current directory if not giving full path or UNC.
        if ($SymName -notmatch '^(?:[a-z]:\\)|(?:\\\\\w+\\[a-z]\$)')
        {
            $SymName = '{0}\{1}' -f $pwd, $SymName
        }

        $flag = @{
            File = 0
            Directory = 1
        }

        if ($PScmdlet.ShouldProcess($Path, 'Create symbolic link'))
        {
            try
            {
                $return = [mklink.symlink]::CreateSymbolicLink($SymName, $Path, $flag[$PScmdlet.ParameterSetName])

                if ($return)
                {
                    $object = New-Object PSObject -Property @{
                        SymLink = $SymName
                        Target = $Path
                        Type = $PScmdlet.ParameterSetName
                    }

                    $object.pstypenames.insert(0, 'System.File.SymbolicLink')
                    $object
                }
                else
                {
                    throw 'Unable to create symbolic link'
                }
            }
            catch
            {
                Write-Warning ('{0}: {1}' -f $path, $_.Exception.Message)
            }
        }
    }
}