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.

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

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.

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

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)

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

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

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