├── Invoke-ElevatedCommand.ps1 ├── README.md ├── envy.psm1 ├── install.ps1 └── settings.psm1 /Invoke-ElevatedCommand.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | ## 3 | ## Invoke-ElevatedCommand 4 | ## 5 | ## From Windows PowerShell Cookbook (O'Reilly) 6 | ## by Lee Holmes (http://www.leeholmes.com/guide) 7 | ## 8 | ############################################################################## 9 | <# 10 | 11 | .SYNOPSIS 12 | 13 | Runs the provided script block under an elevated instance of PowerShell as 14 | through it were a member of a regular pipeline. 15 | 16 | .EXAMPLE 17 | 18 | PS > Get-Process | Invoke-ElevatedCommand.ps1 { 19 | $input | Where-Object { $_.Handles -gt 500 } } | Sort Handles 20 | 21 | #> 22 | 23 | param( 24 | ## The script block to invoke elevated 25 | [Parameter(Mandatory = $true)] 26 | [ScriptBlock] $Scriptblock, 27 | 28 | ## Any input to give the elevated process 29 | [Parameter(ValueFromPipeline = $true)] 30 | $InputObject, 31 | 32 | ## Switch to enable the user profile 33 | [switch] $EnableProfile 34 | ) 35 | 36 | begin 37 | { 38 | Set-StrictMode -Version 3 39 | $inputItems = New-Object System.Collections.ArrayList 40 | } 41 | 42 | process 43 | { 44 | $null = $inputItems.Add($inputObject) 45 | } 46 | 47 | end 48 | { 49 | ## Create some temporary files for streaming input and output 50 | $outputFile = [IO.Path]::GetTempFileName() 51 | $inputFile = [IO.Path]::GetTempFileName() 52 | 53 | ## Stream the input into the input file 54 | $inputItems.ToArray() | Export-CliXml -Depth 1 $inputFile 55 | 56 | ## Start creating the command line for the elevated PowerShell session 57 | $commandLine = "" 58 | if(-not $EnableProfile) { $commandLine += "-NoProfile " } 59 | 60 | ## Convert the command into an encoded command for PowerShell 61 | $commandString = "Set-Location '$($pwd.Path)'; " + 62 | "`$output = Import-CliXml '$inputFile' | " + 63 | "& {" + $scriptblock.ToString() + "} 2>&1; " + 64 | "`$output | Export-CliXml -Depth 1 '$outputFile'" 65 | 66 | $commandBytes = [System.Text.Encoding]::Unicode.GetBytes($commandString) 67 | $encodedCommand = [Convert]::ToBase64String($commandBytes) 68 | $commandLine += "-EncodedCommand $encodedCommand" 69 | 70 | ## Start the new PowerShell process 71 | $process = Start-Process -FilePath (Get-Command powershell).Definition ` 72 | -ArgumentList $commandLine -Verb RunAs ` 73 | -WindowStyle Hidden ` 74 | -Passthru 75 | $process.WaitForExit() 76 | 77 | ## Return the output to the user 78 | if((Get-Item $outputFile).Length -gt 0) 79 | { 80 | Import-CliXml $outputFile 81 | } 82 | 83 | ## Clean up 84 | [Console]::WriteLine($outputFile) 85 | # Remove-Item $outputFile 86 | Remove-Item $inputFile 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 0-to-code 2 | ## Getting from total zero to Visual Studio Code 3 | 4 | If you follow any tutorials on modern web development, it's easy to get lost in an abundance of cryptic incantations involving "npm", "bower", "git" and "yo". It's especially challenging if you are using Windows and are used to the Visual Studio IDE tooling: it's *different*. 5 | 6 | This set of scripts will help you to get all tools that you need to be productive with web development using the tools produced by NodeJS/npm, bower and yo communities, along with the newest kid on the block - Visual Studio Code. 7 | 8 | ### Principles of this toolset 9 | 10 | 1. One script to install all tooling that you need. 11 | 2. All tools will be installed for current user only, whenever possible. 12 | 3. Limited user accounts are supported and UAC elevations are done as part of the installation. 13 | 4. All necessary environment variable changes are made on the user account level. 14 | 15 | ### What is installed 16 | 17 | 1. Chocolatey - this is the magic that makes "one script" installation possible. 18 | 2. ConEmu - you should be ready to the fact that a lot of things will be done in the console, this is a great alternative to a good ole CMD. 19 | 3. NodeJS and NPM - the method of delivery for all these great tools which you'll be using later. 20 | 4. Default set of modules installed globally - bower, yo and its ASP.NET generator, typescript, tsd (access to DefinitelyTyped goodness), gulp and grunt. 21 | 5. GIT. 22 | 6. .NET vNext. 23 | 7. Visual Studio Code. 24 | 25 | ## How to install 26 | 27 | Download this repository's contents as ZIP, unzip it and run this from the command line: 28 | 29 | ``` 30 | powershell -Command "& { dir *.ps*1 | Unblock-File }" 31 | powershell -File .\install.ps1 32 | ``` 33 | 34 | First line will remove blocking from scripts downloaded from Internet. Second line will actually install the software. 35 | 36 | If PowerShell complains on disabled scripts, run the command shown below and then repeat the previous command: 37 | 38 | ``` 39 | powershell -Command "& { Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned }" 40 | ``` 41 | 42 | You might be seeing a couple of UAC elevation prompts - this is expected: ConEmu and NodeJS are installed globally. At the same time, NPM (package manager for NodeJS) is configured to use paths that are local to the currently logged in user, so "npm install XXX -g" will install "global" module locally to your user account. 43 | 44 | By default the installation will use your user profile folder as a "root". If you want to change this, please edit $global:root variable in settings.psm1 before running install.ps1. Be aware that .NET vNext will install into your user profile folder regardless. 45 | 46 | After installation completed, please close the command prompt window and open another one, or better yet, start ConEmu. This is required, because changes in the environment variables will not be picked up otherwise. You should expect commands like "choco", "bower", "yo", "npm", "git" and "code" to be available in the new console session. 47 | 48 | Start coding! 49 | -------------------------------------------------------------------------------- /envy.psm1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS 4 | 5 | Manipulates machine, user or process environment. 6 | 7 | .EXAMPLE 8 | 9 | PS > Get-Var -scope Machine -name PROCESSOR_REVISION 10 | PS > Set-Var -scope User -name TEST -value "Hello world" 11 | PS > Get-Path -scope Process 12 | PS > Add-Path -scope User -path "C:\test" 13 | PS > Add-Path -scope Machine -path "C:\test" -prefix "PSModule" 14 | PS > Remove-Path -scope User -path "C:\test" 15 | 16 | #> 17 | 18 | function Get-Var { 19 | [CmdletBinding()] 20 | param( 21 | [Parameter(Mandatory = $true)] 22 | [System.EnvironmentVariableTarget]$scope, 23 | [Parameter(Mandatory = $false)] 24 | [String]$name 25 | ) 26 | 27 | if ($name -eq ""){ 28 | [System.Environment]::GetEnvironmentVariables($scope).GetEnumerator() | Sort-Object Name 29 | } 30 | else { 31 | [System.Environment]::GetEnvironmentVariable($name, $scope) 32 | } 33 | } 34 | 35 | function Set-Var { 36 | [CmdletBinding()] 37 | param( 38 | [Parameter(Mandatory = $true)] 39 | [System.EnvironmentVariableTarget]$scope, 40 | [Parameter(Mandatory = $true)] 41 | [String]$name, 42 | [Parameter(Mandatory = $false)] 43 | [String]$value 44 | ) 45 | 46 | [System.Environment]::SetEnvironmentVariable($name, $value, $scope) 47 | } 48 | 49 | function Get-Path { 50 | [CmdletBinding()] 51 | param( 52 | [Parameter(Mandatory = $true)] 53 | [System.EnvironmentVariableTarget]$scope, 54 | [Parameter(Mandatory = $false)] 55 | [String]$prefix 56 | ) 57 | 58 | $paths = SplitPath -scope $scope -prefix $prefix 59 | 60 | $paths | foreach { 61 | $pathOut = New-Object psobject -Property @{ 62 | Exists = ([System.IO.DirectoryInfo] $_).Exists 63 | Path = $_ 64 | } 65 | 66 | $pathOut 67 | } | Format-Table @{Expression={$_.Exists};Label="Exists";Width=10;Alignment="Left"},Path 68 | } 69 | 70 | function Add-Path { 71 | [CmdletBinding()] 72 | param( 73 | [Parameter(Mandatory = $true)] 74 | [System.EnvironmentVariableTarget]$scope, 75 | [Parameter(Mandatory = $true)] 76 | [String]$path, 77 | [Parameter(Mandatory = $false)] 78 | [String]$prefix 79 | ) 80 | 81 | $paths = SplitPath -scope $scope -prefix $prefix 82 | $needle = NormalizePath -path $path 83 | 84 | $hasPath = $paths | foreach { 85 | $p = NormalizePath -path $_ 86 | if ($p -like $needle) { 87 | $true 88 | return 89 | } 90 | } 91 | 92 | if ($hasPath -ne $true) { 93 | $paths += $path 94 | SetPath -scope $scope -value ($paths -join ";") -prefix $prefix 95 | } 96 | } 97 | 98 | function Remove-Path { 99 | [CmdletBinding()] 100 | param( 101 | [Parameter(Mandatory = $true)] 102 | [System.EnvironmentVariableTarget]$scope, 103 | [Parameter(Mandatory = $true)] 104 | [String]$path, 105 | [Parameter(Mandatory = $false)] 106 | [String]$prefix 107 | ) 108 | 109 | $paths = SplitPath -scope $scope -prefix $prefix 110 | $needle = NormalizePath -path $path 111 | 112 | $filteredPaths = $paths | foreach { 113 | $p = NormalizePath -path $_ 114 | if (!($p -like $needle)) { 115 | $_ 116 | } 117 | } 118 | 119 | SetPath -scope $scope -value ($filteredPaths -join ";") -prefix $prefix 120 | } 121 | 122 | Export-ModuleMember *-* 123 | 124 | filter Invoke-Ternary ($Predicate, $Then, $Otherwise = $null) { 125 | if ($predicate -is [ScriptBlock]) { $predicate = &$predicate } 126 | 127 | if ($predicate) { 128 | if($then -is [ScriptBlock]) { &$then } 129 | else { $then } 130 | } elseif($otherwise) { 131 | if($otherwise -is [ScriptBlock]) { &$otherwise } 132 | else { $otherwise } 133 | } 134 | } 135 | 136 | filter Invoke-Coalescence($predicate, $alternative) { 137 | if($predicate -is [scriptblock]) { $predicate = &$predicate } 138 | 139 | Invoke-Ternary $predicate $predicate $alternative 140 | } 141 | 142 | set-alias ?: Invoke-Ternary -Option AllScope 143 | set-alias ?? Invoke-Coalescence -Option AllScope 144 | 145 | function SplitPath { 146 | param( 147 | [System.EnvironmentVariableTarget]$scope, 148 | [String]$prefix 149 | ) 150 | 151 | $varprefix = (?? { $prefix } { "" }) 152 | ,((?? { [System.Environment]::GetEnvironmentVariable($varprefix + "path", $scope) } { "" }).Split(";", [StringSplitOptions]::RemoveEmptyEntries)) 153 | } 154 | 155 | function SetPath { 156 | param( 157 | [System.EnvironmentVariableTarget]$scope, 158 | [String]$value, 159 | [String]$prefix 160 | ) 161 | 162 | $varprefix = (?? { $prefix } { "" }) 163 | [System.Environment]::SetEnvironmentVariable($varprefix + "path", $value, $scope) 164 | } 165 | 166 | function NormalizePath{ 167 | param( 168 | [String]$path 169 | ) 170 | 171 | $needle = $path 172 | if (!$needle.EndsWith("\")) { 173 | $needle += "\" 174 | } 175 | 176 | $needle 177 | } 178 | -------------------------------------------------------------------------------- /install.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\settings.psm1 2 | Import-Module .\envy.psm1 3 | 4 | $is64bit = "$env:PROCESSOR_ARCHITECTURE" -like "AMD64" 5 | 6 | # Setup chocolatey in known location. 7 | Set-Var -scope Process -name ChocolateyInstall -value "$chocolateyRoot" 8 | (iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')))>$null 2>&1 9 | Add-Path -scope Process -path "$chocolateyRoot\bin" 10 | 11 | # Setup tools/bin root for portable packages. 12 | Set-Var -scope Process -name ChocolateyBinRoot -value "$chocolateyToolsRoot" 13 | Set-Var -scope User -name ChocolateyBinRoot -value "$chocolateyToolsRoot" 14 | Add-Path -scope Process -path "$chocolateyToolsRoot" 15 | Add-Path -scope User -path "$chocolateyToolsRoot" 16 | 17 | # Allow uninstallers to be executed. 18 | choco feature enable --name=autoUninstaller 19 | 20 | # Setup ConEmu. 21 | Write-Host "Installing ConEmu..." 22 | $chocolateyRoot | .\Invoke-ElevatedCommand.ps1 { &"$input\bin\choco" install conemu -y } 23 | 24 | # Install git. 25 | Write-Host "Installing git..." 26 | $chocolateyRoot | .\Invoke-ElevatedCommand.ps1 { &"$input\bin\choco" install git.install -y -params '"/GitAndUnixToolsOnPath /NoAutoCrlf"' } 27 | 28 | # Help git tools find correct locations. 29 | Set-Var User HOME $root 30 | 31 | $gitRegistryKey = "HKLM:\SOFTWARE\GitForWindows" 32 | $gitKeys = Get-ItemProperty -Path $gitRegistryKey 33 | $gitLocation = $gitKeys.InstallPath 34 | if ($gitLocation -ne "") { 35 | $gitPath = Join-Path $gitLocation "cmd" 36 | Add-Path -scope Process -path "$gitPath" 37 | } 38 | 39 | # Install git credentials manager. 40 | Write-Host "Installing git credentials manager..." 41 | $chocolateyRoot | .\Invoke-ElevatedCommand.ps1 { &"$input\bin\choco" install git-credential-manager-for-windows -y } 42 | git config --global credential.helper manager 43 | 44 | # Install nodejs and configure NPM to use known locations. 45 | Write-Host "Installing nodejs..." 46 | $nodeRoot = "$env:ProgramFiles\nodejs" 47 | $chocolateyRoot | .\Invoke-ElevatedCommand.ps1 { &"$input\bin\choco" install nodejs.install -y --ia "ADDLOCAL=NodeRuntime,npm" } 48 | Add-Path -scope User -path "$nodeRoot" 49 | Add-Path -scope Process -path "$nodeRoot" 50 | 51 | # Setup local NPM cache and repository. 52 | npm config set cache "$npmCache" 53 | npm config set prefix "$npmRepository" 54 | Add-Path -scope User -path "$npmRepository" 55 | 56 | # Install default set of NPM modules. 57 | npm install -g $defaultNpmModules 58 | 59 | # Setup .NET. 60 | &{$Branch='dev';iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.ps1'))} 61 | &"$dnxRoot\bin\dnvm" upgrade 62 | 63 | # Install Visual Studio Code. 64 | $vscodeRoot = "$env:LOCALAPPDATA\Code\bin" 65 | choco install visualstudiocode -y 66 | Add-Path -scope User -path "$vscodeRoot" 67 | 68 | Write-Host "All done! Please close this window and start ConEmu." 69 | -------------------------------------------------------------------------------- /settings.psm1: -------------------------------------------------------------------------------- 1 | # Location for chocolatey and npm repositories. 2 | $global:root = $env:USERPROFILE 3 | 4 | # Known locations. 5 | $global:chocolateyRoot = "$root\.chocolatey" 6 | $global:chocolateyToolsRoot = "$root\.chocolatey\apps" 7 | $global:npmCache = "$root\.npm" 8 | $global:npmRepository = "$root\.npmrepo" 9 | $global:dnxRoot = "$env:USERPROFILE\.dnx" 10 | 11 | # Set of NPM modules installed globally by default. 12 | $global:defaultNpmModules = @("typescript", "yo", "bower", "gulp", "gulp-watch", "gulp-typescript", "gulp-cli", "tsd", "grunt-cli", "generator-aspnet") 13 | --------------------------------------------------------------------------------