Installing node.js on Windows Server 2012 R2 Core

When dealing with Windows Server Core you can still install node.js the same way you would if you had the full GUI. Just copy the node.js msi file to the server and run from the command line. You’ll get the same install prompts you would if the GUI was installed. Select the defaults if you like and away you go!

Node.js_Setup

That’s all well and good for one server. But if you’re installing node.js on Windows Server Core, you probably have more than one. Maybe you have 3 or 4 loadbalanced Core servers. Or maybe you have 30 or 40. Copying the msi file to each and clicking away at the install wizard is not the way to go in that case. When installing anything on more than one Windows 2012 R2 Server Core servers you want to use PowerShell DSC. Here’s a look at how you can install node.js via PowerShell DSC.

The first section of the script is your config section. Normally this would be stored in a separate psd1 file, but for this example I assigned the config data to the $config variable in the same file. The first node in the allNodes array is the “wildcard” node. Config data stored here applies to all the rest of the nodes in the array. The name of the MSI is included as well as the Product ID. The product ID is required. There are various ways to get the product ID of a particular MSI. On a system where Node.js is already installed you can execute the following PowerShell to find the ID.

PS C:\Users\USER001> Get-WmiObject Win32_Product | ? {$_.Name -eq "Node.js"}

IdentifyingNumber : {68EDB54E-2CFB-454E-BBF0-3E41E157E552}
Name              : Node.js
Vendor            : Node.js Foundation
Version           : 6.2.2
Caption           : Node.js

If you don’t have Node.js installed you can also get the Product ID straight from the MSI file with PowerShell if you have the Windows Installer PowerShell Module installed.

PS C:\Users\USER001> Get-MSITable -path .\Downloads\node-v6.2.2-x64.msi -Table Property

Property                     Value
--------                     -----
UpgradeCode                  {47C07A3A-42EF-4213-A85D-8F5A59077C28}
WixUIRMOption                UseRM
WIXUI_INSTALLDIR             INSTALLDIR
ALLUSERS                     1
ARPPRODUCTICON               NodeIcon
ApplicationFolderName        nodejs
DefaultUIFont                WixUI_Font_Normal
WixUI_Mode                   FeatureTree
WIXUI_EXITDIALOGOPTIONALTEXT Node.js has been successfully installed.
Manufacturer                 Node.js Foundation
ProductCode                  {68EDB54E-2CFB-454E-BBF0-3E41E157E552}
ProductLanguage              1033
ProductName                  Node.js
ProductVersion               6.2.2
ErrorDialog                  ErrorDlg
SecureCustomProperties       EARLY_IO_DETECTED;NODE_0X_DETECTED;WIX_DOWNGRADE_DETECTED;WIX_UPGRADE_DETECTED
PS C:\Users\USER001>

Here the product ID is called ProductCode. This is the value you’ll plug into your DSC configuration. The rest of the nodes in the allNodes array are the individual servers I want to install to. There are three listed in this example but it could just as easily be 30.
Then there is a configuration section that I’ve named Install_Node, where node equals all nodes where install equals true. That configuration gets executed to build the mof files and then the DSC configuration is started. Here’s what it looks like running against just one server. I’m doing just one server to make the output cleaner and easier to follow, but this will run against as many servers as you want to simultaneously.

PS C:\Users\USER001> .\installNode.ps1


    Directory: C:\Users\USER001\Install_Node


Mode                LastWriteTime     Length Name                                                                                                                                                                                                                
----                -------------     ------ ----                                                                                                                                                                                                                
-a---         6/29/2016   9:20 AM       1410 server01.mof                                                                                                                                                                                                          
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer DSCSERVER01 with user sid S-1-5-21-759580286-1836913787-1093625069-111464.
VERBOSE: [SERVER01]: LCM:  [ Start  Set      ]
VERBOSE: [SERVER01]: LCM:  [ Start  Resource ]  [[Package]node.js]
VERBOSE: [SERVER01]: LCM:  [ Start  Test     ]  [[Package]node.js]
VERBOSE: [SERVER01]:                            [[Package]node.js] Validate-StandardArguments, Path was \\fileserver01\node-v6.2.2-x64.msi
VERBOSE: [SERVER01]:                            [[Package]node.js] The path extension was .msi
VERBOSE: [SERVER01]:                            [[Package]node.js] Parsing 68EDB54E-2CFB-454E-BBF0-3E41E157E552 as an identifyingNumber
VERBOSE: [SERVER01]:                            [[Package]node.js] Parsed 68EDB54E-2CFB-454E-BBF0-3E41E157E552 as {68EDB54E-2CFB-454E-BBF0-3E41E157E552}
VERBOSE: [SERVER01]:                            [[Package]node.js] Ensure is Present
VERBOSE: [SERVER01]:                            [[Package]node.js] product is
VERBOSE: [SERVER01]:                            [[Package]node.js] product as boolean is False
VERBOSE: [SERVER01]:                            [[Package]node.js] The package Node.js is not installed
VERBOSE: [SERVER01]: LCM:  [ End    Test     ]  [[Package]node.js]  in 0.3750 seconds.
VERBOSE: [SERVER01]: LCM:  [ Start  Set      ]  [[Package]node.js]
VERBOSE: [SERVER01]:                            [[Package]node.js] Validate-StandardArguments, Path was \\fileserver01\node-v6.2.2-x64.msi
VERBOSE: [SERVER01]:                            [[Package]node.js] The path extension was .msi
VERBOSE: [SERVER01]:                            [[Package]node.js] Parsing 68EDB54E-2CFB-454E-BBF0-3E41E157E552 as an identifyingNumber
VERBOSE: [SERVER01]:                            [[Package]node.js] Parsed 68EDB54E-2CFB-454E-BBF0-3E41E157E552 as {68EDB54E-2CFB-454E-BBF0-3E41E157E552}
VERBOSE: [SERVER01]:                            [[Package]node.js] Ensure is Present
VERBOSE: [SERVER01]:                            [[Package]node.js] product is
VERBOSE: [SERVER01]:                            [[Package]node.js] product as boolean is False
VERBOSE: [SERVER01]:                            [[Package]node.js] The package Node.js is not installed
VERBOSE: [SERVER01]:                            [[Package]node.js] Validate-StandardArguments, Path was \\fileserver01\node-v6.2.2-x64.msi
VERBOSE: [SERVER01]:                            [[Package]node.js] The path extension was .msi
VERBOSE: [SERVER01]:                            [[Package]node.js] Parsing 68EDB54E-2CFB-454E-BBF0-3E41E157E552 as an identifyingNumber
VERBOSE: [SERVER01]:                            [[Package]node.js] Parsed 68EDB54E-2CFB-454E-BBF0-3E41E157E552 as {68EDB54E-2CFB-454E-BBF0-3E41E157E552}
VERBOSE: [SERVER01]:                            [[Package]node.js] Package configuration starting
VERBOSE: [SERVER01]:                            [[Package]node.js] Mount share to get media
VERBOSE: [SERVER01]:                            [[Package]node.js] Starting C:\Windows\system32\msiexec.exe with /i "\\fileserver01\node-v6.2.2-x64.msi" /quiet
VERBOSE: [SERVER01]:                            [[Package]node.js] Starting process C:\Windows\system32\msiexec.exe with arguments /i "\\fileserver01\node-v6.2.2-x64.msi" /quiet
VERBOSE: [SERVER01]:                            [[Package]node.js] Package has been installed
VERBOSE: [SERVER01]:                            [[Package]node.js] Package configuration finished
VERBOSE: [SERVER01]: LCM:  [ End    Set      ]  [[Package]node.js]  in 20.3910 seconds.
VERBOSE: [SERVER01]: LCM:  [ End    Resource ]  [[Package]node.js]
VERBOSE: [SERVER01]: LCM:  [ End    Set      ]    in  26.3748 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 22.523 seconds

And there you have it. Node.js installed on Windows Server 2012 R2 Core using DSC. In a future post I’ll show how to install iisnode the same way.

NPM and the Corporate Proxy

After installing node.js on my Windows 10 workstation at the office I tried to install some packages via npm and was greeted with the following error.

PS C:\Users\USER001> npm install handlebars -g
npm ERR! Windows_NT 10.0.10586
npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "install" "handlebars" "-g"
npm ERR! node v6.2.2
npm ERR! npm  v3.9.5
npm ERR! code ECONNREFUSED
npm ERR! errno ECONNREFUSED
npm ERR! syscall connect

npm ERR! Error: connect ECONNREFUSED 23.235.44.162:443
npm ERR!     at Object.exports._errnoException (util.js:1007:11)
npm ERR!     at exports._exceptionWithHostPort (util.js:1030:20)
npm ERR!     at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1080:14)
npm ERR!  { Error: connect ECONNREFUSED 23.235.44.162:443
npm ERR!     at Object.exports._errnoException (util.js:1007:11)
npm ERR!     at exports._exceptionWithHostPort (util.js:1030:20)
npm ERR!     at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1080:14)
npm ERR!   code: 'ECONNREFUSED',
npm ERR!   errno: 'ECONNREFUSED',
npm ERR!   syscall: 'connect',
npm ERR!   address: '23.235.44.162',
npm ERR!   port: 443 }
npm ERR!
npm ERR! If you are behind a proxy, please make sure that the
npm ERR! 'proxy' config is set properly.  See: 'npm help config'

npm ERR! Please include the following file with any support request:
npm ERR!     C:\Users\USER001\npm-debug.log
PS C:\Users\USER001>

To set the proxy use the following commands. Be sure to change the IP address and TCP port number to match your proxy server’s IP and port.

PS C:\Users\USER001> npm config set proxy http://10.10.10.10:8080
PS C:\Users\USER001> npm config set https-proxy http://10.10.10.10:8080

But if your corporate proxy is doing SSL interception and inspection you may still have another problem. You may get this certificate error if your proxy is using a certificate issued by a non-publicly trusted CA.

PS C:\Users\USER001> npm install handlebars -g
npm ERR! Windows_NT 10.0.10586
npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "install" "handlebars" "-g"
npm ERR! node v6.2.2
npm ERR! npm  v3.9.5
npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY

npm ERR! unable to get local issuer certificate
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR!     <https://github.com/npm/npm/issues>

npm ERR! Please include the following file with any support request:
npm ERR!     C:\Users\USER001\npm-debug.log
PS C:\Users\USER001>

There are two ways to handle this. The easier but less secure way to handle this is to configure npm to not use strict-ssl.

PS C:\Users\USER001> npm config set strict-ssl false

But a better and more secure way to resolve this error would be to configure npm to point to a pem file that contains the all the certificates in the certificate chain that your proxy server is using. On Windows make sure to double up the backslashes.

PS C:\Users\USER001> npm config set cafile="C:\\Certificates\\CAChain.pem"

When creating the pem file make sure that the certificate that the proxy server uses is listed first, followed by one or more intermediate certificates, and end with the root CA’s certificate.