Microsoft.PowerShell.NanoServer.SDK.psm1
Set-StrictMode -Version Latest $Script:DotNetCoreRefAsmPath = "${env:SystemDrive}\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore" $Script:NanoPSRefAsmInfoFile = "NanoPSReferenceAssemblyInfo.xml" $Script:HasAdminPrevilege = $null $Script:CmdletSnippet = @" using System; using System.Management.Automation; namespace {0} {{ [Cmdlet("Verb", "Noun")] [Alias("Alias")] public class Class1 : PSCmdlet {{ [Parameter(Position = 0, ValueFromPipeline = true)] public string Param {{ get; set; }} protected override void BeginProcessing() {{ base.BeginProcessing(); }} protected override void ProcessRecord() {{ base.ProcessRecord(); }} protected override void EndProcessing() {{ base.EndProcessing(); }} }} }} "@ ## Load localized error strings Import-LocalizedData LocalizedData -filename NanoPowerShellSDK.Resource.psd1 #region "Utilities" ## ## Test if the current user has admin previlege ## function Test-AdminPrevilege { if ($null -eq $Script:HasAdminPrevilege) { $Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $Principal = New-Object -TypeName System.Security.Principal.WindowsPrincipal -ArgumentList $Identity $Script:HasAdminPrevilege = $Principal.IsInRole("Administrators") } return $Script:HasAdminPrevilege } ## ## Update the project file ## function Update-ProjectFile { param( [string] $ProjectFile, [string] $OutputType ) $Xml = [xml] (Get-Content -Path $ProjectFile) @($Xml.Project.PropertyGroup)[0].TargetFrameworkVersion = "v0.1" @($Xml.Project.PropertyGroup)[0].OutputType = $OutputType @($Xml.Project.Import)[-1].Project = "Microsoft.CoreSys.CSharp.targets" ## Remove references $null = $Xml.Project.RemoveChild(@($Xml.Project.ItemGroup)[0]) Save-Xml -XmlDoc $Xml -Path $ProjectFile } ## ## Update the sample code in Class1.cs ## function Update-SampleCode { param( [string] $SampleFile, [ValidateSet("Library", "EXE")] [string] $OutputType ) if ($OutputType -eq "Library") { $Namespace = Get-Content -Path $SampleFile | ? { $_ -like "namespace *" } | % { ($_ -split ' ')[1] } $SampleCode = $Script:CmdletSnippet -f $Namespace ## Overwrite Class1.cs with sample cmdlet code [System.IO.File]::WriteAllText($SampleFile, $SampleCode, [System.Text.Encoding]::UTF8) } } ## ## Save XmlDocument to the specified file ## function Save-Xml { param( [xml] $XmlDoc, [string] $Path ) $XmlWriterSetting = New-Object -TypeName System.Xml.XmlWriterSettings $XmlWriterSetting.Indent = $true $XmlWriterSetting.IndentChars = " " $XmlWriterSetting.NewLineChars = "`r`n" $XmlWriterSetting.NewLineHandling = "Replace" try { $XmlWriter = [System.Xml.XmlWriter]::Create($Path, $XmlWriterSetting) $XmlDoc.Save($XmlWriter) } finally { $XmlWriter.Close() } } ## ## Utility to write terminating error ## function ThrowError { param( [parameter(Mandatory = $true)] [System.Management.Automation.PSCmdlet] $CallerPSCmdlet, [parameter(Mandatory = $true)] [System.String] $ExceptionName, [parameter(Mandatory = $true)] [System.String] $ExceptionMessage, [System.Object] $ExceptionObject, [parameter(Mandatory = $true)] [System.String] $ErrorId, [parameter(Mandatory = $true)] [System.Management.Automation.ErrorCategory] $ErrorCategory ) $exception = New-Object $ExceptionName $ExceptionMessage; $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject $CallerPSCmdlet.ThrowTerminatingError($errorRecord) } ## ## Utility to wait the copying process with a pesudo progress bar ## function Wait-WithPesudoProgressBar { param( [parameter(Mandatory = $true)] [System.Diagnostics.Process] $Process ) $Count = 10 $Activity = 'NanoServer PowerShell SDK Initial Setup' $Status = 'Copy NanoServer PowerShell Reference Assemblies' Write-Progress -Activity $Activity -Status $Status -PercentComplete $Count while (-not $Process.HasExited) { Start-Sleep -Milliseconds 150 if ($Count -lt 99) { $Count = $Count + (100 - $Count) / 10 } Write-Progress -Activity $Activity -Status $Status -PercentComplete $Count } Write-Progress -Activity $Activity -Status $Status -Completed } #endregion "Utilities" ## ## Create a new CSharp project targeting CoreCLR used by Nano PowerShell ## function New-NanoCSharpProject { <# .SYNOPSIS Creates a new Visual Studio C# project targeting CoreCLR and PowerShell Core included in the Windows Server 2016 Technical Preview 5 release of Nano Server. .DESCRIPTION This cmdlet helps you to create Visual Studio C# projects targeting CoreCLR and PowerShell Core included in the Windows Server 2016 Technical Preview 5 release of Nano Server. - You can choose to create a new project in a new solution, or add a new project to the currently open solution. - You can choose to have the project output either a DLL library or EXE program. NOTE: Please make sure the following Visual Studio prerequisites are installed prior to using the cmdlets: - Visual Studio 2015 Update 2 - Windows and Web Development -> Universal Windows App Development Tools -> Tools (1.3.1) and Windows 10 SDK This cmdlet should be run in the Package Manager Console in Visual Studio 2015. - If you don't have Visual Studio 2015 installed, you can install Visual Studio Community 2015 from https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx This cmdlet first checks if initial setup of the SDK is done. If not, it will copy the reference assemblies to the reference assembly folder of Visual Studio 2015. .EXAMPLE PS C:\> New-NanoCSharpProject -Path D:\Projects -ProjectName DISMCmdlet -SolutionName DISM -OutputType Library This command creates a new C# project in a new solution targeting CoreCLR and PowerShell Core in Nano Server. The new project produces a DLL. .EXAMPLE PS C:\> New-NanoCSharpProject -Path D:\Temp -ProjectName Project2 -AddToCurrentSolution -OutputType EXE -Verbose This command creates a new C# project in the currently open solution targeting CoreCLR and PowerShell Core in Nano Server. The new project produces an EXE. The Verbose parameter displays verbose messages in the output. .PARAMETER Path Specifies the destination path for the new Visual Studio C# project. .PARAMETER ProjectName Specifies the name of the new Visual Studio C# project. .PARAMETER SolutionName Specifies the name of the new Visual Studio solution. .PARAMETER OutputType Specifies the output file type of the new Visual Studio project. Valid values are Library and EXE. .PARAMETER AddToCurrentSolution Creates the new Visual Studio project in the currently open solution. #> [CmdletBinding(DefaultParameterSetName = "NewSolution")] [Alias("nproj")] param( [parameter(Mandatory=$true, Position = 0)] [string] $Path, [parameter(Mandatory=$true, Position = 1)] [string] $ProjectName, [parameter(ParameterSetName = "NewSolution", Position = 2)] [ValidateNotNullOrEmpty()] [string] $SolutionName, [Parameter(Position = 3)] [ValidateSet("Library", "EXE")] [string] $OutputType = "Library", [parameter(ParameterSetName = "CurrentSolution")] [switch] $AddToCurrentSolution ) ## ## Cmdlet needs to run in Package Management Console in Visual Studio ## if ($Host.Name -ne "Package Manager Host") { $Message = $LocalizedData.NeedToRunInVisualStudio -f $LocalizedData.InstallVisualStudio2015 ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "SupportedInPackageManagerConsoleOnly" ` -CallerPSCmdlet $PSCmdlet ` -ExceptionObject $PSCmdlet ` -ErrorCategory InvalidOperation } ## ## Visual Studio 2015 is required for authoring/debugging CoreCLR code ## if ($DTE.Name -ne "Microsoft Visual Studio" -or $DTE.Version -ne "14.0") { $Message = $LocalizedData.RequireVisualStudio2015 -f $LocalizedData.InstallVisualStudio2015 ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "RequireVisualStudio2015" ` -CallerPSCmdlet $PSCmdlet ` -ExceptionObject $PSCmdlet ` -ErrorCategory InvalidOperation } ## ## Do initial setup if needed ## $CoreCLRRefPath = Join-Path -Path $Script:DotNetCoreRefAsmPath -ChildPath v0.1 $RefAsmInfoPath = Join-Path -Path $CoreCLRRefPath -ChildPath $Script:NanoPSRefAsmInfoFile $ModuleBasePath = $PSCmdlet.MyInvocation.MyCommand.Module.ModuleBase if (-not (Test-Path -Path $RefAsmInfoPath -PathType Leaf)) { $Message = $LocalizedData.AboutToDeployReferenceAssemblies -f $Script:DotNetCoreRefAsmPath Write-Verbose -Message $Message $SourceRefPath = Join-Path -Path $ModuleBasePath -ChildPath SDK\v0.1 $ScriptToRun = @' $PreferenceCopy = $ErrorActionPreference try {{ $ErrorActionPreference = "Stop" $WriteProgress = $Host.Name -eq 'Package Manager Host' $Activity = 'NanoServer PowerShell SDK Initial Setup' if (-not (Test-Path -Path '{0}' -PathType Container)) {{ ## Target 'v0.1' folder not exists $null = New-Item -Path '{0}' -ItemType Directory -Force }} else {{ ## Remove old reference assemblies from 'v0.1' folder if ($WriteProgress) {{ $Status = 'Remove Old CoreCLR Reference Assemblies' $ItemsToRemove = @(Get-ChildItem -Path '{0}') $ItemCount = $ItemsToRemove.Count for ($Index = 0; $Index -lt $ItemCount; $Index++) {{ Write-Progress -Activity $Activity -Status $Status -PercentComplete ($Index/$ItemCount*100) Remove-Item -Path $ItemsToRemove[$Index].FullName -Recurse -Force }} Write-Progress -Activity $Activity -Status $Status -Completed }} else {{ Remove-Item -Path '{0}\*' -Recurse -Force }} }} ## Copy TP5 reference assemblies to 'v0.1' folder if ($WriteProgress) {{ $Status = 'Copy TP5 NanoServer PowerShell Reference Assemblies' $ItemsToCopy = @(Get-ChildItem -Path '{1}') $ItemCount = $ItemsToCopy.Count for ($Index = 0; $Index -lt $ItemCount; $Index++) {{ Write-Progress -Activity $Activity -Status $Status -PercentComplete ($Index/$ItemCount*100) Copy-Item -Path $ItemsToCopy[$Index].FullName -Destination '{0}' -Recurse -Force }} Write-Progress -Activity $Activity -Status $Status -Completed }} else {{ Copy-Item -Path '{1}\*' -Destination '{0}' -Recurse -Force }} ## Write the info file [pscustomobject]@{{ NanoPS = 'TP5' }} | Export-Clixml -Path '{2}' -Force Get-Item '{2}' | % {{ $_.Attributes = [System.IO.FileAttributes]::Hidden }} }} finally {{ $ErrorActionPreference = $PreferenceCopy }} '@ -f $CoreCLRRefPath, $SourceRefPath, $RefAsmInfoPath if (Test-AdminPrevilege) { $ScriptBlockToRun = [scriptblock]::Create($ScriptToRun) & $ScriptBlockToRun } else { $Message = $LocalizedData.NeedAdminPrevilegeMessage -f $Script:DotNetCoreRefAsmPath $Caption = $LocalizedData.NeedAdminPrevilegeCaption if (-not $PSCmdlet.ShouldContinue($Message, $Caption)) { $Message = $LocalizedData.InitialSetupCancelled -f $LocalizedData.FollowManualSteps Write-Warning -Message $Message return } $ByteArray = [System.Text.Encoding]::Unicode.GetBytes($ScriptToRun) $Base64EncodedScript = [System.Convert]::ToBase64String($ByteArray) try { $proc = Start-Process powershell -ArgumentList "-EncodedCommand $Base64EncodedScript" -Verb RunAs -WindowStyle Hidden -PassThru Wait-WithPesudoProgressBar -Process $proc if ($proc.ExitCode -ne 0) { $Message = $LocalizedData.StartProcessExecutionFailed -f $LocalizedData.FollowManualSteps ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "FailedToCopyReferenceAssemblies" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation } } catch { $Message = $LocalizedData.StartProcessFailedToStart -f $_.Exception.Message, $LocalizedData.FollowManualSteps ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "FailedToStartAWorkingProcess" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation ` } } $Message = $LocalizedData.DoneDeployingReferenceAssemblies -f $CoreCLRRefPath Write-Verbose -Message $Message } ## ## Create the NanoPS CSharp Project ## $Message = $LocalizedData.AboutToCreateCSharpProject -f $ProjectName Write-Verbose -Message $Message try { ## Fetch the project template Write-Verbose -Message $LocalizedData.FetchProjectTemplate $TemplatePath = $DTE.Solution.GetProjectTemplate("Windows\Class Library", "CSharp") ## Create the project if ($PSCmdlet.ParameterSetName -eq "NewSolution") { if (-not $PSCmdlet.MyInvocation.BoundParameters.ContainsKey("SolutionName")) { $SolutionName = $ProjectName } $Message = $LocalizedData.CreateProjectWithNewSolution -f $SolutionName Write-Verbose -Message $Message $SolutionDir = Join-Path -Path $Path -ChildPath $SolutionName $ProjectDir = Join-Path -Path $SolutionDir -ChildPath $ProjectName if (-not (Test-Path -Path $SolutionDir -PathType Container)) { $null = New-Item -Path $SolutionDir -ItemType Directory -Force } ## If a solution is currently open, close it first if ($DTE.Solution.IsOpen) { $Message = $LocalizedData.CloseCurrentlyOpenSolution -f ($DTE.Solution.Properties.Item("Name").Value) Write-Verbose -Message $Message $DTE.Solution.Close($true) } ## Create the new solution $DTE.Solution.Create($SolutionDir, $SolutionName) $SolutionFilePath = $DTE.Solution.Properties.Item("Path").Value $DTE.Solution.SaveAs($SolutionFilePath) } else { $Message = $LocalizedData.CreateProjectInCurrentSolution -f ($DTE.Solution.Properties.Item("Name").Value) Write-Verbose -Message $Message ## ParameterSet is 'CurrentSolution', but no solution is currently open if (-not $DTE.Solution.IsOpen) { $Message = $LocalizedData.NoCurrentOpenSolution ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "NoExistingOpenSolution" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation } $ExistingProjects = $DTE.Solution.Projects | % ProjectName if ($ProjectName -in $ExistingProjects) { $Message = $LocalizedData.ProjectAlreadyExistsInCurrentSolution -f $ProjectName ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage $Message ` -ErrorId "ProjectAlreadyExists" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation ` -ExceptionObject $ProjectName } $ProjectDir = Join-Path -Path $Path -ChildPath $ProjectName } ## Create the template project $DTE.Solution.AddFromTemplate($TemplatePath, $ProjectDir, $ProjectName, $false) ## Copy *.Targets files and update .csproj file $Message = $LocalizedData.DeployTargetFilesToProject -f $ProjectDir Write-Verbose -Message $Message Copy-Item -Path $ModuleBasePath\SDK\Microsoft.Coresys.Common.Targets -Destination $ProjectDir -Force Copy-Item -Path $ModuleBasePath\SDK\Microsoft.CoreSys.CSharp.Targets -Destination $ProjectDir -Force $ProjectObj = $DTE.Solution.Projects | ? ProjectName -eq $ProjectName $ProjectFile = $ProjectObj.FullName $Class1FileItem = $ProjectObj.ProjectItems | ? Name -eq "Class1.cs" if ($Class1FileItem -eq $null) { ## In some VS instances, 'Solution.AddFromTemplate' doesn't create the placeholder ## file 'Class1.cs'. We manually create the file in this case. $CSharpClassTemplateFile = $DTE.solution.GetProjectItemTemplate("Class", "CSharp") $ProjectObj.ProjectItems.AddFromTemplate($CSharpClassTemplateFile, "Class1.cs") $Class1FileItem = $ProjectObj.ProjectItems | ? Name -eq "Class1.cs" } $Class1FilePath = $Class1FileItem.Properties.Item("FullPath").Value $SolutionExplorer = $DTE.Windows.Item([EnvDTE.Constants]::vsWindowKindSolutionExplorer) ## Save the project if not yet ## Because we are about to unload it if (-not $ProjectObj.Saved) { $ProjectObj.Save() } $Message = $LocalizedData.UpdateProjectFile -f $ProjectFile Write-Verbose -Message $Message ## Unload the project to update the project file $SolutionExplorer.Activate() $ProjectObj.DTE.ExecuteCommand("Project.UnloadProject") ## Update the project file Update-ProjectFile -ProjectFile $ProjectFile -OutputType $OutputType Update-SampleCode -SampleFile $Class1FilePath -OutputType $OutputType ## Reload the project after the update $SolutionExplorer.Activate() $ProjectObj.DTE.ExecuteCommand("Project.ReloadProject") ## Select 'Class1.cs' and open it in code editor $Class1SolutionPath = "$($DTE.Solution.Properties.Item("Name").Value)\$ProjectName\Class1.cs" $SolutionExplorer.Object.GetItem($Class1SolutionPath).Select([EnvDTE.vsUISelectionType]::vsUISelectionTypeSelect) $DTE.ItemOperations.OpenFile($Class1FilePath, [EnvDTE.Constants]::vsViewKindCode) > $null } catch { $Message = $LocalizedData.UnknownDTEFailure -f $_.Exception.Message, $LocalizedData.FollowManualSteps ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "UnknownDTEFailure" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation } $Message = $LocalizedData.DoneCreatingCSharpProject -f $ProjectName Write-Verbose -Message $Message } ## ## Write out the instructions for manually seting up NanoPS SDK and creating C# project targeting it ## function Show-SdkSetupReadMe { <# .SYNOPSIS Opens the SDK root folder in File Explorer and opens the README.txt file for manual setup. .DESCRIPTION This cmdlet opens the SDK root folder in File Explorer. It also opens the README.txt file which contains detailed instructions for setting up the SDK manually and creating Visual Studio C# projects targeting CoreCLR and PowerShell Core included in the Windows Server 2016 Technical Preview 5 release of Nano Server. .EXAMPLE PS C:\> Show-SdkSetupReadMe This command opens the SDK root folder in File Explorer. It also opens the README.txt file which contains detailed instructions for setting up the SDK manually. #> [CmdletBinding()] param() $SdkFolder = Join-Path -Path $PSCmdlet.MyInvocation.MyCommand.Module.ModuleBase -ChildPath SDK $ReadmePath = Join-Path -Path $SdkFolder -ChildPath README.txt Invoke-Item -Path $SdkFolder & "$env:windir\system32\notepad.exe" $ReadmePath } ## ## Install CoreCLR remote debugger to a NanoServer instance ## function Install-RemoteDebugger { <# .SYNOPSIS Installs and configures the Visual Studio remote debugger on a Nano Server machine. .DESCRIPTION This cmdlet copies the Visual Studio remote debugger binaries to a Nano Server machine and configures the debugger to accept connections. After installing the remote debugger, you can use Start-RemoteDebugger and Stop-RemoteDebugger to start and stop the debugger. NOTE: Please make sure the following Visual Studio prerequisites are installed prior to using the cmdlets: - Visual Studio 2015 Update 2 - Windows and Web Development -> Universal Windows App Development Tools -> Tools (1.3.1) and Windows 10 SDK - If you don't have Visual Studio 2015 installed, you can install Visual Studio Community 2015 from https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx .EXAMPLE PS C:\> $session = New-PSSession -ComputerName RemoteNanoServer PS C:\> Install-RemoteDebugger -Session $session The first command creates a PSSession to a remote machine running Nano Server. The second command installs the Visual Studio remote debugger to the Nano Server machine using the PSSession. .PARAMETER Session Specifies a PowerShell remoting session (PSSession) opened to the target Nano Server. You can create a new PSSession using the New-PSSession cmdlet. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession] $Session ) $VSDebuggerBinariesPath = "${env:ProgramFiles(x86)}\Common Files\Microsoft Shared\Phone Tools\14.0\Debugger\target\x64" $VSDebuggerLibPath = "${env:ProgramFiles(x86)}\Common Files\Microsoft Shared\Phone Tools\14.0\Debugger\target\lib" $VSDebuggerOneCorePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio 14.0\VC\redist\onecore" if (-not (Test-Path HKLM:\SOFTWARE\Microsoft\DevDiv\VC\Servicing\14.0)) { $Message = $LocalizedData.RequireVisualStudio2015 -f $LocalizedData.InstallVisualStudio2015 ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "RequireVisualStudio2015" ` -CallerPSCmdlet $PSCmdlet ` -ExceptionObject $PSCmdlet ` -ErrorCategory InvalidOperation } if (-not (Test-Path -Path $VSDebuggerBinariesPath) -or -not (Test-Path -Path $VSDebuggerOneCorePath)) { $Message = $LocalizedData.PrerequisitesForRemoteDebugger ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "PrerequisitesMissing" ` -CallerPSCmdlet $PSCmdlet ` -ExceptionObject $PSCmdlet ` -ErrorCategory InvalidOperation } if (Get-Command -Name Copy-Item -ParameterName ToSession -ErrorAction Ignore) { $CanCopyRemotely = $true if ($Session.State -ne "Opened") { $Message = $LocalizedData.SessionNotOpened ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage $Message ` -ErrorId "SessionNotOpened" ` -CallerPSCmdlet $PSCmdlet ` -ExceptionObject $Session ` -ErrorCategory InvalidArgument } } else { $CanCopyRemotely = $false $Message = $LocalizedData.CannotCopyRemotely -f $PSVersionTable.PSVersion.ToString() Write-Warning -Message $Message } $OldErrorPreference = $ErrorActionPreference try { $ErrorActionPreference = "Stop" $VerbosePrefix = [string]::Empty if ($Host.Name -eq "Package Manager Host") { $VerbosePrefix = "VERBOSE: " } if ($CanCopyRemotely) { ## Create the target folders $RemoteSystemDrive = Invoke-Command -Session $Session -ScriptBlock { $null = New-Item -Path $env:SystemDrive\NanoServerRemoteDebugger -ItemType Directory $null = New-Item -Path $env:SystemDrive\NanoServerRemoteDebugger\CoreCLR -ItemType Directory $env:SystemDrive } $RemoteDebuggerFolder = "$RemoteSystemDrive\NanoServerRemoteDebugger" $RemoteSystem32Folder = "$RemoteSystemDrive\Windows\System32" $RemoteLocalCoreCLRFolder = "$RemoteSystemDrive\NanoServerRemoteDebugger\CoreCLR" $RemoteDotNetCoreFolder = "$RemoteSystemDrive\Windows\System32\DotNetCore\v1.0" $Params = @{ ToSession = $Session } } else { ## Create local temporary folders $TempTargetDir = Join-Path -Path $env:TEMP -ChildPath RemoteDebuggerPack if (Test-Path -Path $TempTargetDir) { Remove-Item -Path $TempTargetDir -Recurse -Force } $RemoteDebuggerFolder = "$TempTargetDir\NanoServerRemoteDebugger" $RemoteSystem32Folder = "$TempTargetDir\System32" $RemoteLocalCoreCLRFolder = "$TempTargetDir\NanoServerRemoteDebugger\CoreCLR" $RemoteDotNetCoreFolder = "$TempTargetDir\DotNetCore" $InstallScriptPath = "$TempTargetDir\install.ps1" $null = New-Item -Path $RemoteDebuggerFolder -ItemType Directory -Force $null = New-Item -Path $RemoteSystem32Folder -ItemType Directory -Force $null = New-Item -Path $RemoteLocalCoreCLRFolder -ItemType Directory -Force $null = New-Item -Path $RemoteDotNetCoreFolder -ItemType Directory -Force $Params = @{} } ## Copy the 64-bit OneCore remote debugger binaries to Nano Server $TargetPath = if ($CanCopyRemotely) { $LocalizedData.CopyToNanoServer -f $RemoteDebuggerFolder } else { $RemoteDebuggerFolder } $Message = $LocalizedData.CopyOneCoreDebuggerBits -f $VerbosePrefix, $TargetPath Write-Verbose -Message $Message Copy-Item @Params -Path "$VSDebuggerBinariesPath\*" -Destination $RemoteDebuggerFolder -Recurse -Force ## Copy platform-agnostic binaries $Message = $LocalizedData.CopyPlatformAgnosticBits -f $VerbosePrefix, $TargetPath Write-Verbose -Message $Message Copy-Item @Params -Path "$VSDebuggerLibPath\*" -Destination $RemoteDebuggerFolder -Recurse -Force ## Copy the CRT (Debug & Release) $TargetPath = if ($CanCopyRemotely) { $LocalizedData.CopyToNanoServer -f $RemoteSystem32Folder } else { $RemoteSystem32Folder } $Message = $LocalizedData.CopyCRTBits -f $VerbosePrefix, $TargetPath Write-Verbose -Message $Message Copy-Item @Params -Path "$VSDebuggerOneCorePath\debug_nonredist\x64\Microsoft.VC140.DebugCRT\vcruntime140d.dll" -Destination $RemoteSystem32Folder -Force Copy-Item @Params -Path "$VSDebuggerOneCorePath\debug_nonredist\x64\Microsoft.VC140.DebugCRT\msvcp140d.dll" -Destination $RemoteSystem32Folder -Force Copy-Item @Params -Path "${env:ProgramFiles(x86)}\Windows Kits\10\bin\x64\ucrt\ucrtbased.dll" -Destination $RemoteSystem32Folder -Force Copy-Item @Params -Path "$VSDebuggerOneCorePath\x64\Microsoft.VC140.CRT\vcruntime140.dll" -Destination $RemoteSystem32Folder -Force Copy-Item @Params -Path "$VSDebuggerOneCorePath\x64\Microsoft.VC140.CRT\msvcp140.dll" -Destination $RemoteSystem32Folder -Force ## Copy local CoreCLR binaries, which are solely required by the CoreCLR remote debugger $TargetPath = if ($CanCopyRemotely) { $LocalizedData.CopyToNanoServer -f $RemoteLocalCoreCLRFolder } else { $RemoteLocalCoreCLRFolder } $Message = $LocalizedData.CopyLocalCoreCLRBits -f $VerbosePrefix, $TargetPath Write-Verbose -Message $Message Copy-Item @Params -Path "${env:CommonProgramFiles(x86)}\Microsoft Shared\Phone Tools\12.0\Debugger\target\x64\*" -Destination $RemoteLocalCoreCLRFolder -Force ## Copy RoMetadata.dll and CoreCLR debugging dlls $ModuleBasePath = $PSCmdlet.MyInvocation.MyCommand.Module.ModuleBase $RometadataPath = Join-Path -Path $ModuleBasePath -ChildPath "Debugger\RoMetadata.dll" $CoreCLRDbgPath = Join-Path -Path $ModuleBasePath -ChildPath "Debugger\CoreCLR" $TargetPath = if ($CanCopyRemotely) { $LocalizedData.CopyToNanoServer -f $RemoteDotNetCoreFolder } else { $RemoteDotNetCoreFolder } $Message = $LocalizedData.CopyCoreCLRDebugBits -f $VerbosePrefix, $TargetPath Write-Verbose -Message $Message Copy-Item @Params -Path $RometadataPath -Destination $RemoteDebuggerFolder -Force Copy-Item @Params -Path "$CoreCLRDbgPath\*" -Destination $RemoteDotNetCoreFolder -Force if ($CanCopyRemotely) { ## Register the debugger on Nano Server $Message = $LocalizedData.ConfigureRemoteDebugger -f $VerbosePrefix Write-Verbose -Message $Message Invoke-Command -Session $Session -ScriptBlock { reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\Appx /v AllowDevelopmentWithoutDevLicense /t REG_DWORD /d 1 /f > $null netsh advfirewall firewall add rule name="Remote Debugger" dir=in action=allow program="$env:SystemDrive\NanoServerRemoteDebugger\msvsmon.exe" enable=yes > $null } } else { ## Generate install.ps1 that does the actuall install on the NanoServer $InstallScript = @' $NanoServerRemoteDebuggerDir = Join-Path -Path $PSScriptRoot -ChildPath NanoServerRemoteDebugger $System32Dir = Join-Path -Path $PSScriptRoot -ChildPath System32 $DotNetCoreDir = Join-Path -Path $PSScriptRoot -ChildPath DotNetCore Copy-Item -Path $NanoServerRemoteDebuggerDir -Destination "$env:SystemDrive\" -Recurse -Force Copy-Item -Path "$System32Dir\*" -Destination "$env:SystemDrive\Windows\System32" -Recurse -Force Copy-Item -Path "$DotNetCoreDir\*" -Destination "$env:SystemDrive\Windows\System32\DotNetCore\v1.0" -Recurse -Force reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\Appx /v AllowDevelopmentWithoutDevLicense /t REG_DWORD /d 1 /f > $null netsh advfirewall firewall add rule name="Remote Debugger" dir=in action=allow program="$env:SystemDrive\NanoServerRemoteDebugger\msvsmon.exe" enable=yes > $null '@ [System.IO.File]::WriteAllText($InstallScriptPath, $InstallScript, [System.Text.Encoding]::ASCII) Invoke-Item -Path $TempTargetDir $Message = $LocalizedData.ManualInstallSteps -f $TempTargetDir Write-Warning -Message $Message } } finally { $ErrorActionPreference = $OldErrorPreference } } ## ## Start the remote debugger to accept connection ## function Start-RemoteDebugger { <# .SYNOPSIS Starts the remote debugger on a remote machine running Nano Server. .DESCRIPTION This cmdlet starts the remote debugger on a remote machine running Nano Server. NOTE: Please make sure the following Visual Studio prerequisites are installed prior to using the cmdlets: - Visual Studio 2015 Update 2 - Windows and Web Development -> Universal Windows App Development Tools -> Tools (1.3.1) and Windows 10 SDK - If you don't have Visual Studio 2015 installed, you can install Visual Studio Community 2015 from https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx After the remote debugger is started, you can attach to the debugger from Visual Studio as follows: - Click the "Debug" menu, then select "Attach to Process..." to open the Attach to Process window - From the Transport drop-down menu, select "Remote (no authentication)" - In the Qualifier field, type the IP address of the remote Nano Server machine - Click the Refresh button to refresh the list of available processes - From the list of available processes, select the process you want to attach to, and then click "Attach" - For example, attach to "wsmprovhost.exe" to debug a module that is running in a remote PowerShell session .EXAMPLE PS C:\> $session = New-PSSession -ComputerName RemoteNanoServer PS C:\> Start-RemoteDebugger -Session $session The first command creates a PSSession to a remote machine running Nano Server. The second command starts the Visual Studio remote debugger on the target Nano Server machine using the PSSession. .PARAMETER Session Specifies a PowerShell remoting session (PSSession) opened to the target Nano Server. You can create a new PSSession using the New-PSSession cmdlet. #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position=0)] [System.Management.Automation.Runspaces.PSSession] $Session ) Invoke-Command -Session $Session -ScriptBlock { Start-Process -FilePath $env:SystemDrive\NanoServerRemoteDebugger\msvsmon.exe -ArgumentList "/nowowwarn /noauth /anyuser /nosecuritywarn /timeout:36000" } } ## ## Stop the remote debugger ## function Stop-RemoteDebugger { <# .SYNOPSIS Stops the remote debugger on a remote machine running Nano Server. .DESCRIPTION This cmdlet stops the remote debugger on a remote machine running Nano Server. NOTE: Please make sure the following Visual Studio prerequisites are installed prior to using the cmdlets: - Visual Studio 2015 Update 2 - Windows and Web Development -> Universal Windows App Development Tools -> Tools (1.3.1) and Windows 10 SDK - If you don't have Visual Studio 2015 installed, you can install Visual Studio Community 2015 from https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx .EXAMPLE PS C:\> $session = New-PSSession -ComputerName RemoteNanoServer PS C:\> Stop-RemoteDebugger -Session $session The first command creates a PSSession to a remote machine running Nano Server. The second command stops the Visual Studio remote debugger on the target Nano Server machine using the PSSession. .PARAMETER Session Specifies a PowerShell remoting session (PSSession) opened to the target Nano Server. You can create a new PSSession using the New-PSSession cmdlet. #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position=0)] [System.Management.Automation.Runspaces.PSSession] $Session ) Invoke-Command -Session $Session -ScriptBlock { Get-Process msvsmon | Stop-Process -force -ErrorAction Stop } } |