├── .gitignore ├── PSShortcut.psd1 ├── PSShortcut.psm1 ├── appveyor.yml └── buildscripts ├── build.ps1 ├── install.ps1 ├── publish.ps1 └── test.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /PSShortcut.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | RootModule = 'PSShortcut.psm1' 3 | ModuleVersion = '1.0' 4 | GUID = '897aef63-e0ad-4cdb-af43-c5cd60a40e9a' 5 | Author = 'Adam Bertram' 6 | CompanyName = 'Adam the Automator, LLC' 7 | Copyright = '(c) 2017 Adam Bertram. All rights reserved.' 8 | Description = 'This module eases working with Windows shortcuts (LNK and URL) files.' 9 | PowerShellVersion = '4.0' 10 | FunctionsToExport = '*' 11 | CmdletsToExport = '*' 12 | VariablesToExport = '*' 13 | AliasesToExport = '*' 14 | PrivateData = @{ 15 | PSData = @{ 16 | 17 | # Tags applied to this module. These help with module discovery in online galleries. 18 | Tags = @('PSModule','Shortcuts') 19 | 20 | # A URL to the license for this module. 21 | # LicenseUri = '' 22 | 23 | # A URL to the main website for this project. 24 | ProjectUri = 'https://github.com/adbertram/PSShortcut' 25 | 26 | # A URL to an icon representing this module. 27 | # IconUri = '' 28 | 29 | # ReleaseNotes of this module 30 | # ReleaseNotes = '' 31 | 32 | } # End of PSData hashtable 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /PSShortcut.psm1: -------------------------------------------------------------------------------- 1 | function Get-Shortcut { 2 | <# 3 | .SYNOPSIS 4 | This function searches for files matching a LNK and URL extension. 5 | .DESCRIPTION 6 | This function, by default, recursively searches for files matching a LNK and URL extensions containing 7 | a specific string inside the target path, name or both. If no folder path specified, it will 8 | recursively search all user profiles and the all users profile. 9 | .NOTES 10 | Created on: 6/23/2014 11 | Created by: Adam Bertram 12 | .EXAMPLE 13 | Get-Shortcut -TargetPath 'http:\\servername\local' 14 | This example would find all shortcuts (URL and LNK) in all user profiles that have a 15 | target path that match 'http:\\servername\local' 16 | .EXAMPLE 17 | Get-Shortcut -TargetPath 'http:\\servername\local' -Name 'name' 18 | This example would find all shortcuts (URL and LNK) in all user profiles that have a 19 | target path that match 'http:\\servername\local' and have a name containing the string "name" 20 | .EXAMPLE 21 | Get-Shortcut -TargetPath 'http:\\servername\local' -FilePath 'C:\Users\abertram\Desktop' 22 | This example would find all shortcuts (URL and LNK) in the 'C:\Users\abertram\Desktop file path 23 | that have a target path that match 'http:\\servername\local' and have a name containing the 24 | string "name" 25 | .PARAMETER TargetPath 26 | The string you'd like to search for inside the shortcut's target path 27 | .PARAMETER Name 28 | A string you'd like to search for inside of the shortcut's name 29 | .PARAMETER FilePath 30 | A string you'd like to search for inside of the shortcut's file path 31 | .PARAMETER FolderPath 32 | The folder path to search for shortcuts in. You can specify multiple folder paths. This defaults to 33 | the user profile root and the all users profile 34 | .PARAMETER NoRecurse 35 | This turns off recursion on the folder path specified searching subfolders of the FolderPath 36 | #> 37 | [CmdletBinding()] 38 | param ( 39 | [string]$TargetPath, 40 | [string]$Name, 41 | [string]$FilePath, 42 | [string[]]$FolderPath, 43 | [switch]$NoRecurse 44 | ) 45 | begin { 46 | function Get-RootUserProfileFolderPath { 47 | <# 48 | .SYNOPSIS 49 | Because sometimes the root user profile folder path can be different this function is a placeholder to find 50 | the root user profile folder path ie. C:\Users or C:\Documents and Settings for any OS. It queries a registry value 51 | to find this path. 52 | #> 53 | [CmdletBinding()] 54 | param () 55 | process { 56 | try { 57 | (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -Name ProfilesDirectory).ProfilesDirectory 58 | } catch { 59 | Write-Error -Message $_.Exception.Message 60 | } 61 | } 62 | } 63 | function Get-AllUsersProfileFolderPath { 64 | <# 65 | .SYNOPSIS 66 | Because sometimes the all users profile folder path can be different this function is a placeholder to find 67 | the all users profile folder path ie. C:\ProgramData or C:\Users\All Users. It uses an environment variable 68 | to find this path. 69 | #> 70 | [CmdletBinding()] 71 | param () 72 | process { 73 | try { 74 | $env:ALLUSERSPROFILE 75 | } catch { 76 | Write-Error -Message $_.Exception.Message 77 | } 78 | } 79 | } 80 | } 81 | process { 82 | try { 83 | if (!$FolderPath) { 84 | $FolderPath = (Get-RootUserProfileFolderPath), (Get-AllUsersProfileFolderPath) 85 | } 86 | 87 | $Params = @{ 88 | 'Include' = @('*.url', '*.lnk'); 89 | 'ErrorAction' = 'SilentlyContinue'; 90 | 'ErrorVariable' = 'MyError'; 91 | 'Force' = $true 92 | } 93 | 94 | if (!$NoRecurse) { 95 | $Params['Recurse'] = $true 96 | } 97 | 98 | [System.Collections.ArrayList]$Shortcuts = @() 99 | 100 | $shell = New-Object -ComObject WScript.Shell 101 | foreach ($Path in $FolderPath) { 102 | try { 103 | Write-Verbose -Message "Searching for shortcuts in $Path..." 104 | [System.Collections.ArrayList]$WhereConditions = @() 105 | $Params['Path'] = $Path 106 | if ($TargetPath) { 107 | if ($TargetPath.StartsWith('http')) { 108 | $TargetPath = $TargetPath -replace '//', '/' 109 | } 110 | $WhereConditions.Add('(($shell.CreateShortcut($_.FullName)).TargetPath -like "*$TargetPath*")') | Out-Null 111 | } 112 | if ($Name) { 113 | $WhereConditions.Add('($_.Name -like "*$Name*")') | Out-Null 114 | } 115 | if ($FilePath) { 116 | $WhereConditions.Add('($_.FullName -like "*$FilePath*")') | Out-Null 117 | } 118 | if ($WhereConditions.Count -gt 0) { 119 | $WhereBlock = [scriptblock]::Create($WhereConditions -join ' -and ') 120 | Get-ChildItem @Params | where $WhereBlock 121 | } else { 122 | Get-ChildItem @Params 123 | } 124 | if ($NewShortcuts) { 125 | $Shortcuts.Add($NewShortcuts) | Out-Null 126 | } 127 | } catch { 128 | Write-Error -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" 129 | $false 130 | } 131 | } 132 | } catch { 133 | Write-Error -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" 134 | $false 135 | } 136 | } 137 | } 138 | 139 | function Set-Shortcut { 140 | <# 141 | .SYNOPSIS 142 | This function modifies a LNK or URL extension shortcut. 143 | .EXAMPLE 144 | PS> Get-Shortcut -TargetPath 'http:\\servername\local' | Set-Shortcut -TargetPath 'http:\\newserver\local' 145 | 146 | This example would find all shortcuts (URL and LNK) in all user profiles that have a 147 | target path that match 'http:\\servername\local' and change that target path to 148 | 'http:\\newserver\local' 149 | .PARAMETER FilePath 150 | One or more file paths to the shortcut file 151 | .PARAMETER TargetPath 152 | The target path you'd like to set in the shortcut 153 | .PARAMETER Comment 154 | The description of the shortcut you'd like to change to 155 | #> 156 | [CmdletBinding(DefaultParameterSetName = 'Default')] 157 | param ( 158 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 159 | [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] 160 | [Alias('Fullname')] 161 | [string[]]$FilePath, 162 | [Parameter(Mandatory, ParameterSetName = 'TargetPath')] 163 | [string]$TargetPath, 164 | [Parameter(Mandatory, ParameterSetName = 'Comment')] 165 | [string]$Comment 166 | ) 167 | process { 168 | try { 169 | $ShellObject = New-Object -ComObject Wscript.Shell 170 | foreach ($File in $FilePath) { 171 | try { 172 | $Shortcut = $ShellObject.CreateShortcut($File) 173 | if ($TargetPath) { 174 | $Shortcut.TargetPath = $TargetPath 175 | } 176 | if ($Comment) { 177 | $Shortcut.Description = $Comment 178 | } 179 | $Shortcut.Save() 180 | } catch { 181 | Write-Error -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" 182 | } 183 | } 184 | } catch { 185 | Write-Error -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | environment: 3 | nuget_apikey: 4 | secure: Bvblx9A9AMTXl4LdQe4Aljoy1WBh48/U3UnlCSxDenF8X6tXEDLqzHdt3krnt7R6 5 | image: WMF 5 6 | install: 7 | - ps: .\buildscripts\install.ps1 8 | build_script: 9 | - ps: .\buildscripts\build.ps1 10 | test_script: 11 | - ps: .\buildscripts\test.ps1 12 | after_test: 13 | - ps: .\buildscripts\publish.ps1 14 | -------------------------------------------------------------------------------- /buildscripts/build.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | 3 | try { 4 | 5 | $manifestFilePath = "$env:APPVEYOR_BUILD_FOLDER\PSShortcut.psd1" 6 | $manifestContent = Get-Content -Path $manifestFilePath -Raw 7 | 8 | ## Update the module version based on the build version and limit exported functions 9 | $replacements = @{ 10 | "ModuleVersion = '.*'" = "ModuleVersion = '$env:APPVEYOR_BUILD_VERSION'" 11 | "FunctionsToExport = '\*'" = "FunctionsToExport = 'Get-Shortcut','Set-Shortcut'" 12 | } 13 | 14 | $replacements.GetEnumerator() | foreach { 15 | $manifestContent = $manifestContent -replace $_.Key,$_.Value 16 | } 17 | 18 | $manifestContent | Set-Content -Path $manifestFilePath 19 | 20 | } catch { 21 | $host.SetShouldExit($LastExitCode) 22 | } -------------------------------------------------------------------------------- /buildscripts/install.ps1: -------------------------------------------------------------------------------- 1 | Write-Host 'Installing necessary PowerShell modules...' 2 | 3 | $provParams = @{ 4 | Name = 'NuGet' 5 | MinimumVersion = '2.8.5.208' 6 | Force = $true 7 | } 8 | 9 | $null = Install-PackageProvider @provParams 10 | $null = Import-PackageProvider @provParams 11 | 12 | $requiredModules = @('Pester','PowerShellGet','PSScriptAnalyzer') 13 | foreach ($m in $requiredModules) { 14 | Write-Host "Installing [$($m)] module..." 15 | Install-Module -Name $m -Force -Confirm:$false 16 | Remove-Module -Name $m -Force -ErrorAction Ignore 17 | Import-Module -Name $m 18 | } -------------------------------------------------------------------------------- /buildscripts/publish.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | 3 | try { 4 | ## Don't upload the build scripts and appveyor.yml to PowerShell Gallery 5 | $tempmoduleFolderPath = "$env:Temp\PSShortcut" 6 | $null = mkdir $tempmoduleFolderPath 7 | 8 | ## Remove all of the files/folders to exclude out of the main folder 9 | $excludeFromPublish = @( 10 | 'PSShortcut\\buildscripts' 11 | 'PSShortcut\\appveyor\.yml' 12 | 'PSShortcut\\\.nuspec' 13 | 'PSShortcut\\\.git' 14 | 'PSShortcut\\README\.md' 15 | 'PSShortcut\\TestResults\.xml' 16 | ) 17 | $exclude = $excludeFromPublish -join '|' 18 | Get-ChildItem -Path $env:APPVEYOR_BUILD_FOLDER -Recurse | where { $_.FullName -match $exclude } | Remove-Item -Force -Recurse 19 | 20 | ## Publish module to PowerShell Gallery 21 | $publishParams = @{ 22 | Path = $env:APPVEYOR_BUILD_FOLDER 23 | NuGetApiKey = $env:nuget_apikey 24 | Repository = 'PSGallery' 25 | Force = $true 26 | Confirm = $false 27 | } 28 | Publish-Module @publishParams 29 | 30 | } catch { 31 | Write-Error -Message $_.Exception.Message 32 | $host.SetShouldExit($LastExitCode) 33 | } -------------------------------------------------------------------------------- /buildscripts/test.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | try { 3 | # Import-Module -Name Pester 4 | # $ProjectRoot = $ENV:APPVEYOR_BUILD_FOLDER 5 | 6 | # $testResultsFilePath = "$ProjectRoot\TestResults.xml" 7 | 8 | # $invPesterParams = @{ 9 | # Path = "$ProjectRoot\PSShortcut.Tests.ps1" 10 | # OutputFormat = NUnitXml 11 | # OutputFile = $testResultsFilePath 12 | # EnableExit = $true 13 | # Tag = 'Unit' 14 | # } 15 | # Invoke-Pester @invPesterParams 16 | 17 | # $Address = "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)" 18 | # (New-Object 'System.Net.WebClient').UploadFile( $Address, $testResultsFilePath ) 19 | } catch { 20 | $host.SetShouldExit($LastExitCode) 21 | } --------------------------------------------------------------------------------