Hello Zeeshan Ilyas
Greetings! Thanks for raising this question in Q&A forum.
Great work setting up a 3-tier architecture on Azure! The issue you're facing is actually a very common and simple PowerShell problem it has nothing to do with your network or VM connectivity. The \n inside your path C:\node-v24.15.0-x64.msi is being interpreted by PowerShell as a newline escape character instead of a backslash. So when PowerShell sees \n, it thinks you're starting a new line and breaks your path, causing the Copy-Item command to fail.
Here's how to fix it step by step:
Step 1: Use a single-quoted string in PowerShell to avoid escape interpretation When you run the command in Run Command, wrap your path in single quotes like this:
Copy-Item 'C:\node-v24.15.0-x64.msi' 'Z:\nodejs.msi'
Single quotes in PowerShell treat everything literally and do not interpret \n, \t, or any other escape sequences.
Step 2: Or use double backslashes as an alternative If you prefer double quotes, escape the backslash by doubling it:
Copy-Item "C:\\node-v24.15.0-x64.msi" "Z:\\nodejs.msi"
Step 3: Skip the Copy-Item approach entirely — use a better method Instead of copying the Node.js installer from fend-vm1 to bend-vm2 using a network share, I strongly recommend you download Node.js directly inside bend-vm2. This is cleaner, simpler, and avoids the network share (Z: drive) mapping issue altogether.
In bend-vm2's Run Command (PowerShell), run this:
Invoke-WebRequest -Uri "https://nodejs.org/dist/v20.11.0/node-v20.11.0-x64.msi" -OutFile "C:\nodejs.msi"
Start-Process msiexec.exe -Wait -ArgumentList '/I C:\nodejs.msi /quiet'
This downloads Node.js straight from the internet into bend-vm2 and installs it silently — no file copying across VMs needed.
Step 4: Make sure bend-vm2 has outbound internet access Since bend-vm2 has no public IP, it needs outbound internet access to download Node.js. By default, Azure VMs in a VNet can reach the internet outbound even without a public IP. You can confirm this by running the following in bend-vm2's Run Command:
Test-NetConnection -ComputerName nodejs.org -Port 443
If it shows TcpTestSucceeded: True, you're good to go.
Step 5: Regarding your Terraform code — it looks great overall! Your custom_data block in the Terraform for bend-vm2 already handles Node.js installation correctly by downloading directly from the internet. The issue is only in the manual Copy-Item command you tried in the portal's Run Command — that is what has the \n problem. Your Terraform approach is actually the right way to go and should work as written.
One small thing to note in your Terraform — custom_data on Windows VMs requires the content to be valid <powershell> script wrapped correctly in the CloudInit format, which your code already does. Just make sure the VM is fully provisioned and the custom_data script has completed before you try to test the Node.js endpoint on port 3000.
Step 6: Test the backend is running after Node.js is installed Once Node.js and your Express server are running on bend-vm2, test it from fend-vm1 using:
Invoke-WebRequest -Uri "http://<bend-vm2-private-ip>:3000" -UseBasicParsing
If you get back the "Hello from Backend VM2" message, everything is working correctly end to end.
If this answer helps you kindly accept the answer which will help others who have similar questions.
Best Regards,
Jerald Felix.