The Power of Automating the Creation of the Secondary Virtual Network Interface Card for Windows Server

July 13, 2020 | 5 minute read
John Parker
Cloud Solution Architect
Text Size 100%:

There are many reasons why you would add a virtual network interface card (VNIC) to a Windows Server instance in Oracle Cloud Infrastructure Compute. This set of instructions allows the full automation in Oracle Cloud to create a Windows Server instance with multiple VNICs that reside in multiple subnets. We cover the current manual steps to create a second VNIC, then take you through the automation pieces necessary to create an instance with a second VNIC. The automation uses a combination of Terraform and PowerShell to create and configure the Windows Server instance.

There are a few items that we don’t discuss in this post. We’re not covering the building of the VCN, subnets, or other infrastructure components. Also, we don’t discuss how to set up your Terraform or other languages or coding scenarios that you can use to create a second VNIC in a Windows server.

The Manual Process

The process outlined in the documentation describes the manual process where once you have the instance up and running you use the console to create the VNIC. Use the following core instructions:

  1. Create the Windows Server instance.

     

  2. Add a secondary VNIC.

     

  3. Download PowerShell script from the Oracle Cloud documentation.

     

  4. Get the Oracle Cloud IDs (OCIDs) of the VNIC from the Console or the Oracle Cloud Metadata service.

     

  5. Execute the PowerShell script as the administrator. This interactive script requires the OCID of the new VNIC.

     

But we’re not here to discuss the manual process. Let’s get to the more interesting perspective of automating the entire process.

Automated Process

As most administrators will tell you, automation is the key to making life easier, allowing administrators to tackle the larger issues that bring more value to the business. You need Terraform components to execute the additional VNIC and PowerShell scripts in the user data and on the host itself. The automated process starts with loading the heavy-lifting PowerShell script into an Object Storage bucket and creating a preauthorized resource (PAR). That process is not covered in this description. Next, create the terraform. Then modify the user data script that’s executed when the instance is created.

Load the new second VNIC PowerShell script into an object store and create a PAR link to the script. You can use the Oracle Cloud command line interface or the Console instructions. The PowerShell script that activates the new VNIC gathers the necessary metadata from the Oracle Cloud Metadata service.

Second_Vnic.ps1
#ps1_sysnative
# Copyright (c) 2018, Oracle and/or its affiliates.
# The Universal Permissive License (UPL), Version 1.0
#
# Oracle OCI Virtual Cloud Networks IP configuration script
#
# 2018-05-11 initial release
#
# Additional Update
# 2020-06-06 by john.s.parker@oracle.com
# Changes to the Code
#   Removed the prompts so that the script runs without the need for interaction, this will
#   overwrite the current network configuration.
#   Gather the metadata for the second VNIC
#   Added a transcript to the code to create a log of events
# variables
# $vnic - metadata for the second VNIC
# $vnicOcid - The OCID for the second vnic
# $metadataServiceVnicsUrl - All the vnic metadata for the instance
# $dnsIPAddress - primary DNS address 169.254.169.254
# $wc Web Clint data
# $allVnicsMetadata - All vnic data information
 
Start-Transcript -Path C:\OCIdata\additionalvnic.txt
# Gather the metatdata for the second VNIC
$vnic = Invoke-RestMethod http://169.254.169.254/opc/v1/vnics/1
$vnicOcid = $vnic.vnicId
 
#
# Constants
#
$metadataServiceVnicsUrl = "http://169.254.169.254/opc/v1/vnics/"
$dnsIPAddress = "169.254.169.254"
#
# Query Metadata Service
#
$wc = New-Object system.Net.WebClient -ErrorAction Stop;
$allVnicsMetadata = ($wc.downloadString($metadataServiceVnicsUrl) | ConvertFrom-Json -ErrorAction Stop);
#
# List available Secondary VNICs if OCID was not provided
#
if (!$vnicOcid)
{
    if ($allVnicsMetadata.Length -le 1)
    {
        Write-Error "Couldn't find any secondary VNICs attached to this instance."
        Exit 1
    }
}
 
#
# Obtain the metadata for the specified VNIC
#
$secondaryVnicMetadata = ($allVnicsMetadata | Where-Object {$_.vnicId -eq $vnicOcid});
if (!$secondaryVnicMetadata)
{
    Write-Error "Couldn't find a VNIC attached to this instance with the specified OCID."
    Exit 1
}
 
$subnetPrefix = ([int]($secondaryVnicMetadata.subnetCidrBlock.split("/")[1]));
 
#
# Find the network adapter to be configured
#
$secondaryVnicAdapter = (Get-NetAdapter -ErrorAction Stop | Where-Object {$_.MacAddress -eq $secondaryVnicMetadata.macAddr.Replace(":", "-")})
if( @($secondaryVnicAdapter).Count -ne 1)
{
    $secondaryVnicAdapter = (Get-NetAdapter -ErrorAction Stop | Where-Object {$_.Vlanid -eq $secondaryVnicMetadata.vlanTag -and $_.InterfaceDescription -like "Microsoft Network Adapter Multiplexor Driver #*"})
}
 
if (!$secondaryVnicAdapter)
{
    Write-Error "Couldn't find a Network Interface for the specified VNIC."
    Exit 1
}
#
# Configure the Network Interface
#
$mac = Get-NetAdapter -Name $secondaryVnicAdapter.Name | select -expand MacAddress
if ($mac -ne $secondaryVnicMetadata.macAddr.Replace(":", "-"))
{
    Set-NetAdapter -Name $secondaryVnicAdapter.Name -MacAddress $secondaryVnicMetadata.macAddr.Replace(":", "-")
}
 
$secondaryVnicAdapterIPConfig = (Get-NetIPAddress -InterfaceAlias $secondaryVnicAdapter.InterfaceAlias -AddressFamily IPv4 -ErrorAction SilentlyContinue);
if ($secondaryVnicAdapterIPConfig)
{
    Remove-NetIPAddress -InterfaceAlias $secondaryVnicAdapter.InterfaceAlias -AddressFamily IPv4 -Confirm true
}
 
New-NetIPAddress -InterfaceAlias $secondaryVnicAdapter.InterfaceAlias -AddressFamily IPv4 -IPAddress $secondaryVnicMetadata.privateIp -PrefixLength $subnetPrefix -ErrorAction Stop
Set-DnsClientServerAddress -InterfaceAlias $secondaryVnicAdapter.InterfaceAlias -ServerAddresses $dnsIPAddress -ErrorAction Stop
 
$secondaryVnicAdapterDefaultRoute = (Get-NetRoute -InterfaceAlias $secondaryVnicAdapter.InterfaceAlias -DestinationPrefix 0.0.0.0/0 -ErrorAction SilentlyContinue);
if ($secondaryVnicAdapterDefaultRoute)
{
    Remove-NetRoute -InterfaceAlias $secondaryVnicAdapter.InterfaceAlias -DestinationPrefix 0.0.0.0/0 -ErrorAction Stop -Confirm true
}
 
New-NetRoute -InterfaceAlias $secondaryVnicAdapter.InterfaceAlias -DestinationPrefix 0.0.0.0/0 -NextHop $secondaryVnicMetadata.virtualRouterIp -ErrorAction Stop | Out-Null
 
# Remove the job
Unregister-ScheduledJob -Name Addvnic
 
Stop-Transcript

Adjust the transform for creating the host with another resource to attach the VNIC. Ensure that you have the subnet ID for the second subnet.

Oracle Cloud Core VNIC Attachment
resource "oci_core_vnic_attachment" "Bastionvnic2" {
    #Required
    create_vnic_details {
        #Required
        subnet_id = oci_core_subnet.subnet2.id
 
 
        #Optional
 
        display_name = var.subnet2_name
        assign_public_ip = "false"
 
        hostname_label = var.hostname_1
        nsg_ids = []
    }
    instance_id = oci_core_instance.Bastion.id
 
}

The following code block is the full resource block for creating the Windows instance, including the user_data and the oci_core_vnic_attachment sections.

Terraform Instance Resouce
resource "oci_core_instance" "Bastion" {
  availability_domain = var.availability_domain1
  fault_domain        = var.instance_fault_domain_1
  compartment_id      = var.compartment_id
  display_name        = var.hostname_1
  shape               = var.instance_shape
  subnet_id           = oci_core_subnet.subnet1.id
  hostname_label      = var.hostname_1
 
  metadata = {
    user_data = base64encode(file("/Users/mymac/Scripts/Powershell/bastionvnic.ps1"))
  }
 
  source_details {
    source_type = "image"
  }
 
  lifecycle {
    ignore_changes = [
      source_details[0].source_id,
    ]
  }
}
resource "oci_core_vnic_attachment" "Bastionvnic2" {
    #Required
    create_vnic_details {
        #Required
        subnet_id = oci_core_subnet.subnet2.id
 
 
        #Optional
 
        display_name = var.subnet2_name
        assign_public_ip = "false"
 
        hostname_label = var.hostname_1
        nsg_ids = []
    }
    instance_id = oci_core_instance.Bastion.id
 
}

The script for the user data needs two additions: the user data Terraform call and the Powershell script for user data. You can also add other configurations necessary in this script.

The user data Terraform call retrieves the script from the local machine.

User Data 
metadata = {
  user_data = base64encode(file("/Users/mymac/Scripts/Powershell/bastionvnic.ps1"))
}

The PowerShell script for user data uses the PAR as the location for the heavy-lifting script that will be ran via a job on the next reboot. I used Invoke-WebRequest to get the file out of object storage and into the newly created Windows Server. The process has a five-minute wait to ensure that all the Oracle Cloud-Init processes finish before rebooting the Windows Server.

BastionVnic.ps1
#ps1_sysnative
#
# Created by John S Parker
# Created on June 15, 2020
mkdir c:\OCIdata;
Start-Transcript -Path "C:\OCIdata\SQLInstall.txt";
# Insure that Invoke-WebRequest is able to execute
# by setting the correct security protocol version
# The PAR comes from the OBJECT store
Invoke-Webrequest -uri $Addvnic -OutFile C:\OCIdata\vnicadd.ps1;
# Create the Job that will update the secondary vnic on the next reboot
$trigger = New-JobTrigger -AtStartup -RandomDelay 00:00:45;
Register-ScheduledJob -Trigger $trigger -FilePath C:\OCIdata\vnicadd.ps1 -Name Addvnic;
Get-ScheduledJobOption -Name Addvnic | Set-ScheduledJobOption -RunElevated;
 
# Reboot the host to execute the job for adding the new vnic
# The sleep time is to ensure all initial processes have completed before rebooting
start-sleep -s 300;
Stop-Transcript;
Restart-Computer -ComputerName "localhost" -Force;
#

Conclusion

You can automate your creation of a secondary VNIC on your Oracle Cloud Windows Server with a little Terraform and some PowerShell. For more resources on adding secondary VNICs and Terraform, check out the Oracle Cloud documentation and the following helpful links:

John Parker

Cloud Solution Architect

I am currently a Cloud Solutions Architect in the Oracle Cloud Infrastructure Compute team.

Oracle Chatbot
Disconnected