Creating a Symbolic Link using PowerShell

Recently, I had a need to create a symbolic link while running a scan on some systems and found that there was not a native way in PowerShell to accomplish this. Now I can use mklink.exe to make this work, but this will only work under cmd.exe. This means that I cannot actually call mklink.exe under PowerShell.

mklink.exe

image

Instead, I have to first call cmd.exe followed by mklink.exe in order to access the command.

cmd.exe /c mklink.exe

image

So you might think that I would be satisfied with this result. Well, I am not. I shouldn’t have to rely on calling cmd.exe first before using mklink.exe to create a symbolic link. And thanks to using Pinvoke to get into the Win32 API, I do not have to accept this fate!

Going out to pinvoke.net, I see under the kernel32 that there is a function called CreateSymbolicLink that meets what I am looking for.

image

 

The basics of what I need are below:

[DllImport("kernel32.dll")]
public static extern bool CreateSymbolicLink(strin

I had to add the ‘public’ keyword at the beginning so this function will be available when I add it into my session. This isn’t all of the code, just what was available on the site. I had to do a little C# coding to make this into a type that I can import into my PowerShell session using Add-Type.

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);
    }
}
"@
[mklink.symlink]

[mklink.symlink] | get-member -static

image

Now I have a type that can be used to create a symbolic link. All it requires is a Path to use for the link and the actual SymName and then either a 0 (File) or a 1 (Directory) to decide what kind of symbolic link will be created. The resulting value is a boolean value that can be easily added into a If/Else statement to handle the rest of the actions.

[mklink.symlink]::CreateSymbolicLink('C:\Users\Administrator\Desktop\SQL2008Install',
"\\dc1\SharedStorage\SQL 2008",1)

image

Looking at the SQL2008Install symbolic link, you see by the attributes that it is a symbolic link as it is labeled as a ReparsePoint:

image

With that, I now have a symbolic link on my desktop that points to the SQL2008 installation folder on my remote server.

Being that this method requires some work it is only natural that this can become a function that is re-usable.

New-SymLink

This function basically does what I have shown you, but in a function form that is portable and more easy to use. This allows you to create a symbolic link for either a directory or a file.

The Begin part of my function involves checking to see if I have already loaded up my pinvoke type and if not, adding the type using Add-Type.

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);
            }
        }
"@
    }
}

The Process block (I am allowing pipeline input for the Path parameter) is where I use my type to create the symbolic link and watch for any issues during this process. As an added item, I will output an object showing the symbolic link and the target path that the link is referencing.

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)
        }
    }
}

Lets see this in action!

New-SymLink -Path "C:\Users\Administrator\Downloads" -SymName Downloads -Directory -Verbose

image

And with that, the function creates a symbolic link for my downloads folder on my desktop. The link to download this function is below. Let me know what you think of this function!

Download

Script Repository

This entry was posted in powershell and tagged , , , . Bookmark the permalink.

15 Responses to Creating a Symbolic Link using PowerShell

  1. BW says:

    I’m trying to make a symbolic link to a shadow copy.
    I can do this using mklink, e.g.:
    mklink /D c:\temp\SB_C5_1 “\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy7”
    (it will not be directly accessable through explorer, but robocopy can access it)
    I cannot do this using you function, e.g.:
    New-Symlink -Path \?\GLOBALROOT\Device\HarddiskVolumeShadowCopy7 -SymName “c:\temp\SBL_1” -Directory -Verbose
    I get an error saying the path doesn’t exist.
    Any way to get this to work?

  2. Brian Wengel says:

    it seems you now can use the New-Item cmdlet
    e.g.: New-Item -Path C:\LinkDir -ItemType SymbolicLink -Value F:\RealDir
    https://msdn.microsoft.com/powershell/reference/5.1/microsoft.powershell.management/New-Item

    • Aleš Kapl says:

      Unfortunately, New-Item has some limitations. For example it can’t make symlink from shadow copy too… It seems that we must wait for some next PS version for complete implementation of functionality from MKLINK. ;o(

  3. Woter says:

    Hi Boe,
    I finally understand what parameter sets are all about. Thanks. Anyway, do you know if this “should” support mapped drives? Specifically an NFS mount (using NFS-Client feature on Server 2012 R2)? Currently, I’m getting the error “WARNING: N:: Unable to create symbolic link!”. N:\ is the mapped drive.

    Thanks.

  4. sangandongo says:

    Thanks so much for your script. This is killer… I am, however, running into an issue.

    I’ve been attempting to run this over a PSSession. I run a loop in which I connect to a pool of servers each in turn, run the script, then disconnect the session. I load the New-SymLink function on each iteration, since it needs be loaded into each session.

    Using New-SymLink to create a local symlink works normally. For example, “new-symlink -symname c:\foo2 -path c:\foo” works great. But, if I attempt to symlink to a network location through the session, “new-symlink -SymName C:\foo2 -Path \server\foo”, it fails with the following error:

    Access is denied
    New-SymLink : Cannot validate argument on parameter ‘Path’. ‘\server\foo’ doesn’t exist!

    UAC is not enabled on these servers. Invoking mklink via cmd functions normally. I was hoping to do an all Powershell script to create these network path symlinks. I’ll fall back on the CMD version of my script for the time being, but I’d love to know if you have a suggestion to get around this issue.

    Thanks,
    j.k.k.

  5. Tim says:

    You are a wonderful human being.

  6. Anonymous says:

    It should be pointed out that mklink is not an exe, it is part of the cmd shell which is why it does not work from within powershell.

  7. Pingback: Renaming Users’ Profiles | PhilSismondo's

  8. Pingback: Create a Hard Link Using PowerShell | Learn Powershell | Achieve More

  9. Jaap Brasser says:

    Cool script, better than calling mklink or junction from PowerShell which is what I have been doing in the past. Perhaps a check for local administrative credentials or the #Requires -RunAsAdministrator tag could be used as the current error for not running with administrative credentials is not very descriptive.

    Nice function!

  10. Hi Boe!
    The PowerShell community module PSCX contains ready to use cmdlets for HardLink, Junction Points or SymLinks.
    See my german Article (you can translate it with the Translate Button on my Page)
    http://www.admin-source.de/BlogDeu/38/verknuepfungen-im-ntfs-dateisystem-hardlink-junction-point-oder-symlink-mit-powershell

    greets Peter Kriegel
    http://www.admin-source.de

    • Boe Prox says:

      Yes, that module is a great one and has those cmdlets available. But for those who cannot use 3rd party modules in their environment, this provides a way to still have be able to create the symbolic links without the need of the module. Also, this article provides a way to show people how this was built and where I was able to come up with the code to do so.

Leave a reply to Alexandru Cancel reply