Software Defined Data Center

Overview

A software-defined data center (SDDC) is an environment in which all resources of the infrastructure - networking, storage, compute and security – are virtualized and delivered as a service. Deployment, provisioning, configuration and operation of the entire infrastructure is abstracted from underlying hardware and implemented through software. The four elements of SDDC are:

  • Server Virtualization - A technology which abstracts a guest operating system from the underlying hardware or software. A key use of virtualization technology is server virtualization, which uses a software layer called a hypervisor to emulate the underlying hardware. This often includes the CPU's, memory, I/O and network traffic.

  • Software Defined Storage (SDS) – A technology which is a form of storage virtualization software that provides policy-based provisioning and management of data storage independent of the underlying hardware.

  • Software Defined Networking (SDN) – A technology which is a form of network virtualization that allows network administrators to manage network services through abstraction of lower-level functionality. SDN addresses the fact that the static architecture of traditional networks doesn't support the dynamic, scalable computing and storage needs of more modern computing environments such as data centers.

  • Software Defined Security (SDSec) – A type of security model in which the information security in a computing environment is implemented, controlled and managed by security software through governance and policies.

All these elements of Software-defined data center are addressed in Windows Server 2016 in technologies like Hyper-V, Storage Spaces Direct, Scale-Out File Server, Storage Replica, Network Controller, Nano Server and Guarded Fabric. System Center 2016 Virtual Machine Manager is an end-to-end infrastructure provisioning and management solution for the Software-Defined data center that brings all these technologies together at one central place.

Considerations

For Software Defined Data Center scenario, it is important to have a moderate understanding of:

  • PowerShell
  • Networking
  • Hyper-V
  • Storage
  • Clustering
  • Security

Software Defined Data Center Features

Server Virtualization

Hyper-V is Microsoft's hardware virtualization product. It lets you create and run a software version of a computer, called a virtual machine. Each virtual machine acts like a complete computer, running an operating system and programs. When you need computing resources, virtual machines give you more flexibility, help save time and money, and are a more efficient way to use hardware than just running one operating system on physical hardware. Hyper-V is a role in Windows Server 2016.

Hyper-V runs each virtual machine in its own isolated space, which means you can run more than one virtual machine on the same hardware at the same time. You might want to do this to avoid problems such as a crash affecting the other workloads, or to give different people, groups or services access to different systems.

Hyper-V helps in the following areas:

  • Establish or expand a private cloud environment - Provide more flexible, on-demand IT services by moving to or expanding your use of shared resources and adjust utilization as demand changes.
  • Use your hardware more effectively - Consolidate servers and workloads onto fewer, more powerful physical computers to use less power and physical space.
  • Improve business continuity - Minimize the impact of both scheduled and unscheduled downtime of your workloads.
  • Make development and test more efficient. Reproduce different computing environments without having to buy or maintain all the hardware you'd need if you only used physical systems.

Hyper-V in Windows Server 2016 offers many new or improved features that can help you build SDDC.

Discrete device assignment

This feature lets you give a virtual machine direct and exclusive access to some PCIe hardware devices. Using a device in this way bypasses the Hyper-V virtualization stack, which results in faster access. This enables scenarios such as graphical adapters that may not work with applications via RemoteFX because of unrecognized drivers or types of storage like NVMe devices.

Host resource protection

This feature helps prevent a virtual machine from using more than its share of system resources by looking for excessive levels of activity. This can help prevent a virtual machine's excessive activity from degrading the performance of the host or other virtual machines. When monitoring detects a virtual machine with excessive activity, the virtual machine is given fewer resources.

Hot add and remove for network adapters and memory

You can now add or remove a network adapter while the virtual machine is running, without incurring downtime. This works for generation 2 virtual machines that run either Windows or Linux operating systems. You can also adjust the amount of memory assigned to a virtual machine while it's running, even if you haven't enabled Dynamic Memory. This works for both generation 1 and generation 2 virtual machines.

Integration services delivered through Windows Update

Updates to integration services for Windows guests are distributed through Windows Update. For service providers and private cloud hosters, this puts the control of applying updates into the hands of the tenants who own the virtual machines. Tenants can now update their Windows virtual machines with all updates, including the integration services, using a single method.

Linux Secure Boot

Linux operating systems running on generation 2 virtual machines can now boot with the Secure Boot option enabled. Ubuntu 14.04 and later, SUSE Linux Enterprise Server 12 and later, Red Hat Enterprise Linux 7.0 and later, and CentOS 7.0 and later are enabled for Secure Boot on hosts that run Windows Server 2016.

More memory and processors for generation 2 virtual machines and Hyper-V hosts

Starting with version 8, generation 2 virtual machines can use significantly more memory and virtual processors. Hosts also can be configured with significantly more memory and virtual processors than were previously supported. These changes support new scenarios such as running e-commerce large in-memory databases for online transaction processing (OLTP) and data warehousing (DW).

Networking features

New networking features include:

  • Remote direct memory access (RDMA) and switch embedded teaming (SET). You can set up RDMA on network adapters bound to a Hyper-V virtual switch, regardless of whether SET is also used. SET provides a virtual switch with some of same capabilities as NIC teaming.
  • Virtual machine multi queues (VMMQ). Improves on VMQ throughput by allocating multiple hardware queues per virtual machine. The default queue becomes a set of queues for a virtual machine, and traffic is spread between the queues.
  • Quality of service (QoS) for software-defined networks. Manages the default class of traffic through the virtual switch within the default class bandwidth.
Production checkpoints

Production checkpoints are "point-in-time" images of a virtual machine. These give you a way to apply a checkpoint that complies with support policies when a virtual machine runs a production workload. Production checkpoints are based on backup technology inside the guest instead of a saved state. For Windows virtual machines, the Volume Snapshot Service (VSS) is used. For Linux virtual machines, the file system buffers are flushed to create a checkpoint that's consistent with the file system.

Rolling Hyper-V Cluster upgrade

You can now add a node running Windows Server 2016 to a Hyper-V Cluster with nodes running Windows Server 2012 R2. This allows you to upgrade the cluster without downtime. The cluster runs at a Windows Server 2012 R2 feature level until you upgrade all nodes in the cluster and update the cluster functional level.

Shared virtual hard disks

You can now resize shared virtual hard disks (.vhdx files) used for guest clustering, without downtime. Shared virtual hard disks can be grown or shrunk while the virtual machine is online. Guest clusters can now also protect shared virtual hard disks by using Hyper-V Replica for disaster recovery.

Start order priority for clustered virtual machines

This feature gives you more control over which clustered virtual machines are started or restarted first. This makes it easier to start virtual machines that provide services before virtual machines that use those services. Define sets, place virtual machines in sets, and specify dependencies.

Virtual machine configuration file format

Virtual machine configuration files use a new format that makes reading and writing configuration data more efficient. The format also makes data corruption less likely if a storage failure occurs. Virtual machine configuration data files use a .vmcx file name extension and runtime state data files use a .vmrs file name extension.

Virtual machine configuration version

The version represents the compatibility of the virtual machine's configuration, saved state, and snapshot files with the version of Hyper-V. Virtual machines with version 5 are compatible with Windows Server 2012 R2 and can run on both Windows Server 2012 R2 and Windows Server 2016 . Virtual machines with versions introduced in Windows Server 2016 won't run in Hyper-V on Windows Server 2012 R2.

If you move or import a virtual machine to a server that runs Hyper-V on Windows Server 2016 from Windows Server 2012 R2, the virtual machine's configuration isn't automatically updated. This means you can move the virtual machine back to a server that runs Windows Server 2012 R2. But, this also means you can't use the new virtual machine features until you manually update the version of the virtual machine configuration.

Windows PowerShell Direct

This gives you a way to run Windows PowerShell commands in a virtual machine from the host. Windows PowerShell Direct runs between the host and the virtual machine. This means it doesn't require networking or firewall requirements, and it works regardless of your remote management configuration.

Windows PowerShell Direct is an alternative to the existing tools that Hyper-V administrators use to connect to a virtual machine on a Hyper-V host:

  • Remote management tools such as PowerShell or Remote Desktop
  • Hyper-V Virtual Machine Connection (VMConnect)

Those tools work well, but have trade-offs: VMConnect is reliable, but can be hard to automate. Remote PowerShell is powerful, but can be hard to set up and maintain. These trade-offs may become more important as your Hyper-V deployment grows. Windows PowerShell Direct addresses this by providing a powerful scripting and automation experience that's as simple as using VMConnect.

Software Defined Storage

Storage in Windows Server 2016 provides new and improved features for software-defined datacenter (SDDC) customers focusing on virtualized workloads. Windows Server also provides extensive support for enterprise customers using file servers with existing workloads.

Storage Spaces Direct

Storage Spaces Direct enables building highly available and scalable storage using servers with local storage. It simplifies the deployment and management of software-defined storage systems and unlocks use of new classes of disk devices, such as SATA SSD and NVMe disk devices, that were previously not possible with clustered Storage Spaces with shared disks.

Storage Spaces Direct enables service providers and enterprises to use industry standard servers with local storage to build highly available and scalable software defined storage. Using servers with local storage decreases complexity, increases scalability, and enables use of storage devices that were not previously possible, such as SATA solid state disks to lower cost of flash storage, or NVMe solid state disks for better performance.

Storage Spaces Direct removes the need for a shared SAS fabric, simplifying deployment and configuration. Instead it uses the network as a storage fabric, leveraging SMB3 and SMB Direct (RDMA) for high-speed, low-latency CPU efficient storage. To scale out, simply add more servers to increase storage capacity and I/O performance.

Storage Replica

Storage Replica enables storage-agnostic, block-level, synchronous replication between servers or clusters for disaster recovery, as well as stretching of a failover cluster between sites. Synchronous replication enables mirroring of data in physical sites with crash-consistent volumes to ensure zero data loss at the file-system level. Asynchronous replication allows site extension beyond metropolitan ranges with the possibility of data loss.

Storage Replication enables you to do the following:

  • Provide a single vendor disaster recovery solution for planned and unplanned outages of mission critical workloads.
  • Use SMB3 transport with proven reliability, scalability, and performance.
  • Stretch Windows failover clusters to metropolitan distances.
  • Use Microsoft software end to end for storage and clustering, such as Hyper-V, Storage Replica, Storage Spaces, Cluster, Scale-Out File Server, SMB3, Deduplication, and ReFS/NTFS.
  • Help reduce cost and complexity as follows:

    • Is hardware agnostic, with no requirement for a specific storage configuration like DAS or SAN.
    • Allows commodity storage and networking technologies.
    • Features ease of graphical management for individual nodes and clusters through Failover Cluster Manager.
    • Includes comprehensive, large-scale scripting options through Windows PowerShell.
  • Help reduce downtime, and increase reliability and productivity intrinsic to Windows.
  • Provide supportability, performance metrics, and diagnostic capabilities.
Storage Quality of Service

You can now use storage quality of service (QoS) to centrally monitor end-to-end storage performance and create management policies using Hyper-V and CSV clusters in Windows Server 2016.

Create storage QoS policies on a CSV cluster and assign them to one or more virtual disks on Hyper-V virtual machines. Storage performance is automatically readjusted to meet policies as the workloads and storage loads fluctuate. Storage QoS can be summarized in the following capabilities:

  • Each policy can specify a reserve (minimum) and/or a limit (maximum) to be applied to a collection of data flows, such as a virtual hard disk, a single virtual machine or a group of virtual machines, a service, or a tenant.
  • Using Windows PowerShell or WMI, you can perform the following tasks:

    • Create policies on a CSV cluster.
    • Enumerate policies available on a CSV cluster.
    • Assign a policy to a virtual hard disk of a Hyper-V virtual machine.
    • Monitor the performance of each flow and status within the policy.
  • If multiple virtual hard disks share the same policy, performance is fairly distributed to meet demand within the policy's minimum and maximum settings. Therefore, a policy can be used to manage a virtual hard disk, a virtual machine, multiple virtual machines comprising a service, or all virtual machines owned by a tenant.
SMB hardening improvements for SYSVOL and NETLOGON connections

In Windows 10 and Windows Server 2016 client connections to the Active Directory Domain Services default SYSVOL and NETLOGON shares on domain controllers now require SMB signing and mutual authentication (such as Kerberos). This change reduces the likelihood of man-in-the-middle attacks. If SMB signing and mutual authentication are unavailable, a Windows 10 or Windows Server 2016 computer won't process domain-based Group Policy and scripts.

Health Service

The Health Service is a new feature in Windows Server 2016 that improves the day-to-day monitoring, operations, and maintenance experience of cluster resources on a Storage Spaces Direct cluster.

Fault domain awareness

Failover Clustering enables multiple servers to work together to provide high availability – or put another way, to provide node fault tolerance. But today's businesses demand ever-greater availability from their infrastructure. To achieve cloud-like uptime, even highly unlikely occurrences such as chassis failures, rack outages, or natural disasters must be protected against. That's why Failover Clustering in Windows Server 2016 introduces chassis, rack, and site fault tolerance as well. Fault domain awareness can be used on both Computer and Storage nodes.

Software Defined Networking

Networking is a foundational part of the Software Defined Datacenter (SDDC) platform, and Windows Server 2016 provides new and improved Software Defined Networking (SDN) technologies to help you move to a fully realized SDDC solution for your organization.

When you manage networks as a software defined resource, you can describe an application's infrastructure requirements one time, and then choose where the application runs - on premises or in the cloud. This consistency means that your applications are now easier to scale and you can seamlessly run applications, anywhere, with equal confidence around security, performance, quality of service, and availability.

Network Controller

New in Windows Server 2016, Network Controller provides a centralized, programmable point of automation to manage, configure, monitor, and troubleshoot virtual and physical network infrastructure in your datacenter. Using Network Controller, you can automate the configuration of network infrastructure instead of performing manual configuration of network devices and services.

Network Controller is a highly available and scalable server role, and provides one application programming interface (API) that allows Network Controller to communicate with the network, and a second API that allows you to communicate with Network Controller.

Network Controller communicates with network devices, services, and components by using the Southbound API. With the Southbound API, Network Controller can discover network devices, detect service configurations, and gather all of the information you need about the network. In addition, the Southbound API gives Network Controller a pathway to send information to the network infrastructure, such as configuration changes that you have made.

The Network Controller Northbound API provides you with the ability to gather network information from Network Controller and use it to monitor and configure the network.

The Network Controller Northbound API allows you to configure, monitor, troubleshoot, and deploy new devices on the network by using Windows PowerShell, the Representational State Transfer (REST) API, or a management application with a graphical user interface, such as System Center Virtual Machine Manager.

Network Function Virtualization

In today's software defined datacenters, network functions that are being performed by hardware appliances (such as load balancers, firewalls, routers, switches, and so on) are increasingly being virtualized as virtual appliances. This "network function virtualization" is a natural progression of server virtualization and network virtualization. Virtual appliances are quickly emerging and creating a brand new market. They continue to generate interest and gain momentum in both virtualization platforms and cloud services.

A virtual appliance is dynamic and easy to change because it is a pre-built, customized virtual machine. It can be one or more virtual machines packaged, updated, and maintained as a unit. Together with software defined networking (SDN), you get the agility and flexibility needed in today's cloud-based infrastructure. For example:

  • SDN presents the network as a pooled and dynamic resource.
  • SDN facilitates tenant isolation.
  • SDN maximizes scale and performance.
  • Virtual appliances enable seamless capacity expansion and workload mobility.
  • Virtual appliances minimize operational complexity.
  • Virtual appliances let customers easily acquire, deploy, and manage pre-integrated solutions.

    • Customers can easily move the virtual appliance anywhere in the cloud.
    • Customers can scale virtual appliances up or down dynamically based on demand.

The marketplace for virtualized network functions is growing quickly. The following network functions are being virtualized:

  • Security

    • Firewall
    • Antivirus
    • DDoS (Distributed Denial of Service)
    • IPS/IDS (Intrusion Prevention System/Intrusion Detection System)
  • Application/WAN optimizers
  • Edge

    • Site-to-site gateway
    • L3 gateways
    • Routers
    • Switches
    • NAT
    • Load balancers (not necessarily at the edge)
    • HTTP proxy
Software Load Balancing

Cloud Service Providers (CSPs) and Enterprises that are deploying Software Defined Networking (SDN) in Windows Server 2016 can use Software Load Balancing (SLB) to evenly distribute tenant and tenant customer network traffic among virtual network resources. The Windows Server SLB enables multiple servers to host the same workload, providing high availability and scalability.

Windows Server SLB includes the following capabilities:

  • Layer 4 (L4) load balancing services for 'North-South' and 'East-West' TCP/UDP traffic.
  • Public and Internal network traffic load balancing.
  • Supports dynamic IP addresses (DIPs) on virtual Local Area Networks (VLANs) and on virtual networks that you create by using Hyper-V Network Virtualization.
  • Health probe support.
  • Ready for cloud scale, including scale-out capability, and scale up capability for multiplexers and Host Agents.

Using Windows Server SLB, you can scale out your load balancing capabilities using SLB VMs on the same Hyper-V compute servers that you use for your other VM workloads. Because of this, SLB supports the rapid creation and deletion of load balancing endpoints that is required for CSP operations. In addition, Windows Server SLB supports tens of gigabytes per cluster, provides a simple provisioning model, and is easy to scale out and in.

RAS Gateway for SDN

In Windows Server 2016, RAS Gateway routes network traffic between the physical network and VM network resources, regardless of where the resources are located. You can use RAS Gateway to route network traffic between physical and virtual networks at the same physical location or at many different physical locations over the Internet.

Multitenancy is the ability of a cloud infrastructure to support the virtual machine workloads of multiple tenants, yet isolate them from each other, while all of the workloads run on the same infrastructure. The multiple workloads of an individual tenant can interconnect and be managed remotely, but these systems do not interconnect with the workloads of other tenants, nor can other tenants remotely manage them.

Following are RAS Gateway features in Windows Server 2016. You can deploy RAS Gateway in high availability pools that use all of these features at one time:

  • Site-to-site VPN - This RAS Gateway feature allows you to connect two networks at different physical locations across the Internet by using a site-to-site VPN connection. For CSPs that host many tenants in their datacenter, RAS Gateway provides a multitenant gateway solution that allows your tenants to access and manage their resources over site-to-site VPN connections from remote sites, and that allows network traffic flow between virtual resources in your datacenter and their physical network.
  • Point-to-site VPN - This RAS Gateway feature allows organization employees or administrators to connect to your organization's network from remote locations. For multitenant deployments, tenant network administrators can use point-to-site VPN connections to access virtual network resources at the CSP datacenter.
  • GRE Tunneling - Generic Routing Encapsulation (GRE) based tunnels enable connectivity between tenant virtual networks and external networks. Since the GRE protocol is lightweight and support for GRE is available on most of network devices it becomes an ideal choice for tunneling where encryption of data is not required. GRE support in Site to Site (S2S) tunnels solves the problem of forwarding between tenant virtual networks and tenant external networks using a multi-tenant gateway, as described later in this topic.
  • Dynamic routing with Border Gateway Protocol (BGP) - BGP reduces the need for manual route configuration on routers because it is a dynamic routing protocol, and automatically learns routes between sites that are connected by using site-to-site VPN connections. If your organization has multiple sites that are connected by using BGP-enabled routers such as RAS Gateway, BGP allows the routers to automatically calculate and use valid routes to each other in the event of network disruption or failure.
Datacenter Firewall

Datacenter Firewall is a new service included with Windows Server 2016. It is a network layer, 5-tuple (protocol, source and destination port numbers, source and destination IP addresses), stateful, multitenant firewall. When deployed and offered as a service by the service provider, tenant administrators can install and configure firewall policies to help protect their virtual networks from unwanted traffic originating from Internet and intranet networks.

The service provider administrator or the tenant administrator can manage the Datacenter Firewall policies via the network controller and the northbound APIs.

The Datacenter Firewall offers the following advantages for cloud service providers:

  • A highly scalable, manageable, and diagnosable software-based firewall solution that can be offered to tenants
  • Freedom to move tenant virtual machines to different compute hosts without breaking tenant firewall policies

    • Deployed as a vSwitch port host agent firewall
    • Tenant virtual machines get the policies assigned to their vSwitch host agent firewall
    • Firewall rules are configured in each vSwitch port, independent of the actual host running the virtual machine
  • Offers protection to tenant virtual machines independent of the tenant guest operating system

The Datacenter Firewall offers the following advantages for tenants:

  • Ability to define firewall rules to help protect Internet facing workloads on virtual networks
  • Ability to define firewall rules to help protect traffic between virtual machines on the same L2 virtual subnet as well as between virtual machines on different L2 virtual subnets
  • Ability to define firewall rules to help protect and isolate network traffic between tenant on premise networks and their virtual networks at the service provider
Hyper-V Network Virtualization

Hyper-V Network Virtualization (HNV) enables virtualization of customer networks on top of a shared physical network infrastructure. Introduced in Windows Server 2012, Hyper-V Network Virtualization (HNV) enables virtualization of customer networks on top of a shared physical network infrastructure. With minimal changes, necessary on the physical network fabric, HNV gives service providers the agility to deploy and migrate tenant workloads anywhere across the three clouds: the service provider cloud, the private cloud, or the Microsoft Azure public cloud.

HNV in Windows Server 2016 offers enhanced support in the following areas:

  • Programmable Hyper-V switch - HNV is a fundamental building block of Microsoft's updated Software Defined Networking (SDN) solution, and is fully integrated into the SDN stack. Microsoft's new Network Controller pushes HNV policies down to a Host Agent running on each host using Open vSwitch Database Management Protocol (OVSDB) as the SouthBound Interface (SBI). The Host Agent stores this policy using a customization of the VTEP schema and programs complex flow rules into a performant flow engine in the Hyper-V switch. The flow engine inside the Hyper-V switch is the same engine used in Microsoft Azure, which has been proven at hyper-scale in the Microsoft Azure public cloud. Additionally, the entire SDN stack up through the Network Controller is consistent with Microsoft Azure, thus bringing the power of the Microsoft Azure public cloud to our enterprise and hosting service provider customers.
  • VXLAN encapsulation support - The Virtual eXtensible Local Area Network (VXLAN - RFC 7348) protocol has been widely adopted in the market place, with support from vendors like Cisco, Brocade, Dell, HP and others. HNV also now supports this encapsulation scheme using MAC distribution mode through the Microsoft Network Controller to program mappings for tenant overlay network IP addresses (Customer Address, or CA) to the physical underlay network IP addresses (Provider Address, or PA). Both NVGRE and VXLAN Task Offloads are supported for improved performance through third-party drivers.
  • Compliant IEEE Ethernet headers - HNV implements correct L2 Ethernet headers to ensure interoperability with third-party virtual and physical appliances that depend on industry-standard protocols. Microsoft ensures that all transmitted packets have compliant values in all fields to ensure this interoperability. In addition, support for Jumbo Frames (MTU > 1780) in the physical L2 network will be required to account for packet overhead introduced by encapsulation protocols (NVGRE, VXLAN) while ensuring guest Virtual Machines attached to an HNV Virtual Network maintain a 1514 MTU.

Software Defined Security

Virtualization security is a major investment area in Windows Server 2016 Hyper-V. In addition to protecting hosts or other virtual machines from a virtual machine running malicious software, we also need to protect virtual machines from a compromised host. Since a virtual machine is just a file, shielded VMs can protect it from attacks via the storage system, the network, or while it is backed up. This is a fundamental danger for every virtualization platform today, whether it's Hyper-V, VMware or any other. Quite simply, if a virtual machine gets out of an organization (either maliciously or accidentally), that virtual machine can be run on any other system. Protecting high value assets in your organization, such as domain controllers, sensitive file servers, and HR systems, is a top priority.

To help protect against compromised fabric, Windows Server 2016 Hyper-V introduces shielded VMs. A shielded VM is a generation 2 VM (supported on Windows Server 2012 and later) that has a virtual TPM, is encrypted using BitLocker and can only run on healthy and approved hosts in the fabric. Shielded VMs and guarded fabric enable cloud service providers or enterprise private cloud administrators to provide a more secure environment for tenant VMs.

Example Scenario

Contoso Hosting wants to build their next generation datacenter to meet the requirements of their customers. They want to evaluate a number of software defined data center solutions that must comply with the following requirements:

  • Centralized management for Software defined data center
  • Efficient, resilient and high performing hypervisor
  • Dynamically adjust virtual machine configurations while running
  • Efficient, resilient and high performing storage
  • Monitor and manage storage performance for virtual machines
  • Easy storage scale out and scale up
  • Ability to segregate tenants on compute, storage and network level to prevent attacks from compromised VMs
  • Ability to deploy virtual networking appliances
  • Ability to deploy workloads with complex network policies
  • Protect VM data and state from fabric administrators and untrusted software running on hypervisor

Lab Requirements

In order to successfully execute the scenarios in the next sections the following requirements needs to be met:

  • Physical Server with minimum 8 processors, 128 GBs RAM, 1TB storage , Nested Virtualization capable, Hyper-V role installed
  • Windows Server 2016 Datacenter RTM iso – Evaluation version
  • Syspreped Windows Server 2016 Datacenter (with Desktop Experience) with latest updates VHDx - Evaluation version
  • Syspreped Windows Server 2016 Datacenter (Core) with latest updates VHDx - Evaluation version
  • SCVMM 2016 ISO
  • SQL Server 2016 iso
  • Windows Assessment and Deployment Kit

Setup Environment

To Setup full Software Defined Data center we will use one single Hyper-V host and nested virtualization. On the Hyper-V hosts we will create the following resources

  • 3 Virtual Switches

    • SDN virtual switch – Contains Management network (VLANID 7), Transit Network (VLAND 10), Provider Network (VLANID 11), Public VIP network and Private VIP network
    • Public – Contains Public Network
    • Private – Contains Private Network
  • Domain Controller VM – acts as domain controller for the environment but also router by using RRAS role.
  • 4 Storage Spaces Direct (S2D) Nodes – acts as Hyper-V and Storage nodes
  • Virtual Machine Manager VM – This will be the Virtual Machine Manager.
  • Remote VM – acts as Remote VPN server for testing S2S VPN functionality in SDN.

The diagram below represents those resources that will be deployed on the Hyper-V host.

Additional resources like Network Controller and etc. will be deployed on top of the S2D Cluster.

The table below shows basic configurations for the VMs on the Hyper-V host:

VM Name

Processor Count

Memory

Hard Drives

vNics

DC01

2

4GB

127GB for C:

SDN(MGTM) - IP: 10.0.0.1, MASK: 255.255.255.0, Gateway: 10.0.0.1, DNS: 127.0.0.1

SDN(HNV) - IP: 10.10.56.1, MASK: 255.255.255.0, DNS: 10.0.0.1

SDN(Transit) - IP: 10.10.10.1, MASK: 255.255.255.0, DNS: 10.0.0.1

Public - IP:50.50.50.2, MASK: 255.255.255.0, DNS: 10.0.0.1

S2D01

8

16GB

127GB for C:2x256GB and 3x512GB

SDN(MGTM) - IP: 10.0.0.211, MASK: 255.255.255.0, Gateway: 10.0.0.1, DNS: 10.0.0.1

S2D02

8

16GB

127GB for C:2x256GB and 3x512GB

SDN(MGTM) - IP: 10.0.0.212, MASK: 255.255.255.0, Gateway: 10.0.0.1, DNS: 10.0.0.1

S2D03

8

16GB

127GB for C:2x256GB and 3x512GB

SDN(MGTM) - IP: 10.0.0.213, MASK: 255.255.255.0, Gateway: 10.0.0.1, DNS: 10.0.0.1

S2D04

8

16GB

127GB for C:2x256GB and 3x512GB

SDN(MGTM) - IP: 10.0.0.214, MASK: 255.255.255.0, Gateway: 10.0.0.1, DNS: 10.0.0.1

VMM01

4

4GB

127GB for C:200GB for T:

SDN(MGTM) - IP: 10.0.0.225, MASK: 255.255.255.0, Gateway: 10.0.0.1, DNS: 10.0.0.1

Remote

2

2GB

127GB for C:

Public - IP:50.50.50.1, MASK: 255.255.255.0, DNS: 50.50.50.2

Private - IP:60.60.60.1, MASK: 255.255.255.0, DNS: 60.60.60.1

Create Hyper-V switches

On the Hyper-V host execute the commands below to create 3 Hyper-V switches:

New-VMSwitch -SwitchName "SDN" `

-SwitchType Internal;

New-VMSwitch -SwitchName "Public" `

-SwitchType Internal;

New-VMSwitch -SwitchName "Private" `

-SwitchType Internal;

Sysprep VHDs

On the Hyper-V host deploy two VMs (Generation 2) to sysprep VHDs:

  • VM with Windows Server 2016 Datacenter (with Desktop Experience)
  • VM with Windows Server 2016 Datacenter (core)

Update both VMs with the latest updates. You can use the code below on the VMs to install the latest updates:

# Check for Available Updates

$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate `

-ClassName MSFT_WUOperationsSession;

# Scan for Updates

$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates `

-Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true};

# Show Updates found for install

$result.Updates;

# Initiate Update and restart

$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate `

-ClassName MSFT_WUOperationsSession;

# Apply Updates

Invoke-CimMethod -InputObject $ci `

-MethodName ApplyApplicableUpdates;

# Restart Server

Restart-Computer; exit;

The code requires the VMs to be connected to public Internet.

Enable RDP on both VMs. You can use the code below on the VMs to enable RDP:

Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" `

-Name "fDenyTSConnections" `

-Value 0;

Enable-NetFirewallRule -DisplayGroup "Remote Desktop";

At the end sysprep the VMs by using the following command on them:

C:\Windows\system32\sysprep\sysprep.exe /oobe /generalize /shutdown

Copy the syspreped VHDs on the storage where the VMs will be deployed and delete the VMs.

Create and configure Domain Controller VM

Execute the script below on the Hyper-V host to create DC01 VM. When prompted enter the information needed for VM creation.

# VM name

$VMName = "DC01";

# Storage Path

$StoragePath = Read-Host `

-Prompt 'Enter path to storage where VMs will be stored (example C:\ClusterStorage\Volume1) ';

# OS VHDs location

$osVHDPath = Read-Host `

-Prompt 'Enter syspreped VHDs path (Windows Server 2016 DataCenter (with Desktop Experience) (example C:\ClusterStorage\Volume1\WS2016DC-DE.vhdx) ';

$OSVHDname = $osVHDPath.Split("\")[-1]

# Create VM

New-VM -Name $VMName `

-Path "$StoragePath\" `

-Generation 2 `

-SwitchName "SDN" `

-MemoryStartupBytes 4GB ;

# Set Proc number

Set-VM -Name $VMName `

-ProcessorCount 2;

# Copy OS VHD

Copy-Item -Path $osVHDPath `

-Destination "$StoragePath\$VMName\" `

-Force;

# Add OS vhd to VM

Add-VMHardDiskDrive -VMName $VMName `

-Path "$StoragePath\$VMName\$OSVHDname";

# Get OS VHD

$vhd = Get-VMHardDiskDrive -VMName $VMName;

# Set boot order

Set-VMFirmware -VMName $VMName `

-FirstBootDevice $vhd;

Set-VMNetworkAdapterVlan -VMName $VMName `

-VlanId 7 `

-Access;

# Rname Network adapter

Get-VMNetworkAdapter -VMName $VMName | Rename-VMNetworkAdapter -NewName "MGMT";

Set-VMNetworkAdapter -Name "MGMT" `

-VMName $VMName `

-DeviceNaming On;

# Start VM

Start-VM -Name $VMName;

Once the VM started and you are logged on to DC01 via Hyper-V remote console configure MGMT interface with PowerShell:

# Configure MGMT interface

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 10.0.0.1 `

–PrefixLength 24;

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 127.0.0.1;

Rename the server and restart:

# Rename the server

Rename-Computer -NewName "DC01" `

-Restart;

Set time zone on DC01:

# List Time Zones

tzutil /l | more

# Set Time Zone

tzutil /s "<your time zone>"

Open Server Manager install Active Directory Domain Services and DNS Server roles:

Setup new domain in a new forest and restart. For domain FQDN you can use dms.int for example.

Open Server Manager and from Remote Server Administration tools install Failover Cluster Management Tools, Failover Cluster Module and Hyper-V Management Tools:

Open DNS console on DC01. Select Properties on the DNS Server. On Interfaces tab choose Only the following IP Addresses and check only 10.0.0.1:

On the Hyper-V host add the other virtual network adapters to DC01:

# VM name

$VMName = "DC01";

#Add Additional adapters

Add-VMNetworkAdapter -VMName $VMName `

-SwitchName "SDN" `

-Name "Transit" `

-DeviceNaming On;

Set-VMNetworkAdapterVlan -VMNetworkAdapterName "Transit" `

-VMName $VMName `

-VlanId 10 `

-Access;

Add-VMNetworkAdapter -VMName $VMName `

-SwitchName "SDN" `

-Name "HNV" `

-DeviceNaming On;

Set-VMNetworkAdapterVlan -VMNetworkAdapterName "HNV" `

-VMName $VMName `

-VlanId 11 `

-Access;

Add-VMNetworkAdapter -VMName $VMName `

-SwitchName "Public" `

-Name "Public" `

-DeviceNaming On;

On DC01 configure IP settings for the new adapters:

# Configure Transit interface

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "Transit"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 10.10.10.1 `

–PrefixLength 24;

Get-DnsClient -InterfaceAlias $Interface | Set-DnsClient -RegisterThisConnectionsAddress $false

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 10.0.0.1;

# Configure HNV interface

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "HNV"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 10.10.56.1 `

–PrefixLength 24;

Get-DnsClient -InterfaceAlias $Interface | Set-DnsClient -RegisterThisConnectionsAddress $false

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 10.0.0.1;

# Configure Public interface

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "Public"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 50.50.50.2 `

–PrefixLength 24;

Get-DnsClient -InterfaceAlias $Interface | Set-DnsClient -RegisterThisConnectionsAddress $false

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 10.0.0.1;

On DC01 start Server Manager and install Remote Access role with DirectAccess and VPN and Routing.

Start Routing and Remote Access console right click on the server and select Configure and Enable Routing and Remote Access. From Configuration window select Custom Configuration and click Next. From Custom Configuration window check LAN routing and click Next. On the last window click Finish. Start the service when prompted.

Create and Configure Storage Spaces Direct Nodes

Execute the script below on the Hyper-V host to create Storage Spaces Direct nodes S2D01, S2D02, S2D03 and S2D04. When prompted enter the information needed for VM creation.

# Executed on the Hyper-V host

# VM names

$VMNames = ('S2D01', 'S2D02', 'S2D03', 'S2D04');

# Storage Path

$StoragePath = Read-Host `

-Prompt 'Enter path to storage where VMs will be stored (example C:\ClusterStorage\Volume1) ';

# OS VHDs location

$osVHDPath = Read-Host `

-Prompt 'Enter syspreped VHDs path (Windows Server 2016 DataCenter (Core) (example C:\ClusterStorage\Volume1\WS2016DC-Core.vhdx) ';

$OSVHDname = $osVHDPath.Split("\")[-1]

Foreach($VMName in $VMNames )

{

# Create VM

New-VM -Name $VMName `

-Path "$StoragePath\$VMName" `

-Generation 2 `

-SwitchName "SDN" `

-MemoryStartupBytes 16GB ;

# Set Proc number

Set-VM -Name $VMName `

-ProcessorCount 8;

# Copy OS VHD

Copy-Item -Path $osVHDPath `

-Destination "$StoragePath\$VMName\" `

-Force;

# Add OS vhd to VM

Add-VMHardDiskDrive -VMName $VMName `

-Path "$StoragePath\$VMName\$OSVHDname";

# Get OS VHD

$vhd = Get-VMHardDiskDrive -VMName $VMName;

# Set boot order

Set-VMFirmware -VMName $VMName `

-FirstBootDevice $vhd;

Set-VMNetworkAdapterVlan -VMName $VMName `

-Trunk `

-AllowedVlanIdList 0-11 `

-NativeVlanId 7;

# Rname Network adapter

Get-VMNetworkAdapter -VMName $VMName | Rename-VMNetworkAdapter -NewName "MGMT";

Set-VMNetworkAdapter -Name "MGMT" `

-VMName $VMName `

-DeviceNaming On;

# Start VM

Start-VM -Name $VMName;

};

Once the VMs are started and you are logged on each server via Hyper-V remote console configure MGMT interface with PowerShell:

# Configure MGMT interface on S2D01

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 10.0.0.211 `

–PrefixLength 24 `

-DefaultGateway 10.0.0.1;

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 10.0.0.1;

# Configure MGMT interface on S2D02

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 10.0.0.212 `

–PrefixLength 24 `

-DefaultGateway 10.0.0.1;

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 10.0.0.1;

# Configure MGMT interface on S2D03

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 10.0.0.213 `

–PrefixLength 24 `

-DefaultGateway 10.0.0.1;

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 10.0.0.1;

# Configure MGMT interface on S2D04

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 10.0.0.214 `

–PrefixLength 24 `

-DefaultGateway 10.0.0.1;

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 10.0.0.1;

Set time zone on each server:

# List Time Zones

tzutil /l | more

# Set Time Zone

tzutil /s "<your time zone>"

Rename and join each server to domain:

# Join S2D01 to domain

# Join computer to domain and restart

Add-Computer -DomainName "dms.int" `

-Force `

-Restart `

-NewName "S2D01";

# Join S2D02 to domain

# Join computer to domain and restart

Add-Computer -DomainName "dms.int" `

-Force `

-Restart `

-NewName "S2D02";

# Join S2D03 to domain

# Join computer to domain and restart

Add-Computer -DomainName "dms.int" `

-Force `

-Restart `

-NewName "S2D03";

# Join S2D04 to domain

# Join computer to domain and restart

Add-Computer -DomainName "dms.int" `

-Force `

-Restart `

-NewName "S2D04";

On DC01 create domain group with name S2DAdmins:

Add S2DAdmins domain group in Local Administrators group on S2D01, S2D02, S2D03 and S2D04:

Net localgroup Administrators "dms\S2DAdmins" /add

On the Hyper-V hosts execute the script below to enable Nested Virtualization for S2D01, S2D02, S2D03 and S2D04:

# Execute on Hyper-V host

# VM names

$VMNames = ('S2D01', 'S2D02', 'S2D03', 'S2D04');

Foreach($VMName in $VMNames )

{

# Shut Down the VM

Stop-VM –Name $VMName;

# Enable Nested Virtualization

Set-VMProcessor -VMName $VMName `

-ExposeVirtualizationExtensions $true;

# Enable MAC Address Spoofing

Get-VMNetworkAdapter -VMName $VMName | Set-VMNetworkAdapter -MacAddressSpoofing On;

# Start VM

Start-VM -Name $VMName;

};

Add 5 disks (vhdx) to each Storage Spaces Direct server. At later step, we will change the media type to SSD on two disks on each node in order to simulate having two different tiers for Storage Spaces Direct. Execute the script below on the Hyper-V host to create the disks on the servers:

# Add Additional disks

# Execute on Hyper-V host

$VMNames = ('S2D01', 'S2D02', 'S2D03', 'S2D04');

Foreach ($VMName in $VMNames){

# SSD Disk Names

$SSDDiskNames = ("SSD01.vhdx", "SSD02.vhdx");

# Create and attach SDD disks

foreach ($SSDDiskName in $SSDDiskNames )

{

$diskName = $SSDDiskName;

# Get the VM

$VM = Get-VM -Name $VMName;

# Get VM Location

$VMLocation = $VM.Path;

# Set Disk Size in GB

$Disksize = 256;

$DisksizeinBytes = $Disksize*1024*1024*1024;

# Create Disk

$VHD = New-VHD -Path "$VMLocation\$diskName" `

-Dynamic `

-SizeBytes $DisksizeinBytes;

# Atach the disk

$AddedSharedVHDX = ADD-VMHardDiskDrive -VMName $VM.Name `

-Path "$VMLocation\$diskName" `

-ControllerType SCSI `

-ControllerNumber 0;

};

# HHD Disk Names

$HDDDiskNames = ("HDD01.vhdx", "HDD02.vhdx", "HDD03.vhdx");

# Create and attach HDD disks

foreach ($HDDDiskName in $HDDDiskNames )

{

$diskName = $HDDDiskName;

# Get the VM

$VM = Get-VM -Name $VMName;

# Get VM Location

$VMLocation = $VM.Path;

# Set Disk Size in GB

$Disksize = 512;

$DisksizeinBytes = $Disksize*1024*1024*1024;

# Create Disk

$VHD = New-VHD -Path "$VMLocation\$diskName" `

-Dynamic `

-SizeBytes $DisksizeinBytes;

# Atach the disk

$AddedSharedVHDX = ADD-VMHardDiskDrive -VMName $VM.Name `

-Path "$VMLocation\$diskName" `

-ControllerType SCSI `

-ControllerNumber 0;

};

};

Install File services, Failover Clustering and Hyper-V roles on each of the S2D nodes:

# Install Roles

Install-WindowsFeature -Name File-Services

Install-WindowsFeature -Name Failover-clustering -IncludeManagementTools

Install-WindowsFeature -Name Hyper-V -IncludeManagementTools -Restart

On DC01 execute the following command to validate the cluster before creating it:

# Validate Cluster

$nodes = ("S2D01", "S2D02", "S2D03", "S2D04");

# Validate Cluster Configuration

Test-Cluster –Node $nodes `

–Include "Storage Spaces Direct","Inventory","Network","System Configuration";

You can proceed with cluster creation if there are no failed tests. Execute the below command on DC01 to create the cluster:

# Create Cluster

$nodes = ("S2D01", "S2D02", "S2D03", "S2D04");

# Create Cluster with no storage

New-Cluster –Name S2D-CLU `

–Node $nodes `

–NoStorage `

–StaticAddress 10.0.0.215;

When the cluster is created Storage Spaces Direct can be enabled. This is done through a single command which you can execute from DC01 remotely to the cluster:

# Enable Storage Spaces Direct

Enable-ClusterStorageSpacesDirect -CimSession S2D-CLU `

-Confirm:$false;

By default, Storage Spaces Direct will create tiers based on the disks present on the nodes. Delete the default tier, change the media type on some of the disks to simulate two different tiers and create them by executing the commands below in DC01 remotely to S2D-CLU cluster:

# Remove all Storage Tiers

Get-StorageTier -CimSession S2D-CLU | Remove-StorageTier -Confirm:$false `

-CimSession S2D-CLU;

# Change Media Type

Get-StoragePool -FriendlyName "S2D*" `

-CimSession S2D-CLU | `

Get-PhysicalDisk -CimSession S2D-CLU | `

? Size -lt 300GB ` | `

Set-PhysicalDisk -CimSession S2D-CLU `

–MediaType SSD;

Get-StoragePool -FriendlyName "S2D*" `

-CimSession S2D-CLU | `

Get-PhysicalDisk -CimSession S2D-CLU | `

? Size -gt 300GB | `

Set-PhysicalDisk -CimSession S2D-CLU `

–MediaType HDD;

# Create Tiers

Get-StoragePool -FriendlyName "S2D*" `

-CimSession S2D-CLU | `

New-StorageTier –FriendlyName Performance `

–MediaType SSD `

-ResiliencySettingName Mirror `

-CimSession S2D-CLU;

Get-StoragePool -FriendlyName "S2D*" `

-CimSession S2D-CLU | `

New-StorageTier –FriendlyName Capacity `

–MediaType HDD `

-ResiliencySettingName Parity `

-CimSession S2D-CLU;

Create and Configure VMM Server

Execute the script below on the Hyper-V host to create VMM01 VM. When prompted enter the information needed for VM creation.

# VM name

$VMName = "VMM01";

# Storage Path

$StoragePath = Read-Host `

-Prompt 'Enter path to storage where VMs will be stored (example C:\ClusterStorage\Volume1) ';

# OS VHDs location

$osVHDPath = Read-Host `

-Prompt 'Enter syspreped VHDs path (Windows Server 2016 DataCenter (with Desktop Experience) (example C:\ClusterStorage\Volume1\WS2016DC-DE.vhdx) ';

$OSVHDname = $osVHDPath.Split("\")[-1]

# Create VM

New-VM -Name $VMName `

-Path "$StoragePath\" `

-Generation 2 `

-SwitchName "SDN" `

-MemoryStartupBytes 4GB ;

# Set Proc number

Set-VM -Name $VMName `

-ProcessorCount 4;

# Copy OS VHD

Copy-Item -Path $osVHDPath `

-Destination "$StoragePath\$VMName\" `

-Force;

# Add OS vhd to VM

Add-VMHardDiskDrive -VMName $VMName `

-Path "$StoragePath\$VMName\$OSVHDname";

# Get OS VHD

$vhd = Get-VMHardDiskDrive -VMName $VMName;

# Set boot order

Set-VMFirmware -VMName $VMName `

-FirstBootDevice $vhd;

# Set Disk Size in GB

$Disksize = 200;

$DisksizeinBytes = $Disksize*1024*1024*1024;

# Create Disk

$VHD = New-VHD -Path "$StoragePath\$VMName\HDD01.vhdx" `

-Dynamic `

-SizeBytes $DisksizeinBytes;

# Atach the disk

$AddedVHDX = ADD-VMHardDiskDrive -VMName $VMName `

-Path "$StoragePath\$VMName\HDD01.vhdx" `

-ControllerType SCSI `

-ControllerNumber 0;

Set-VMNetworkAdapterVlan -VMName $VMName `

-VlanId 7 `

-Access;

# Rname Network adapter

Get-VMNetworkAdapter -VMName $VMName | Rename-VMNetworkAdapter -NewName "MGMT";

Set-VMNetworkAdapter -Name "MGMT" `

-VMName $VMName `

-DeviceNaming On;

# Start VM

Start-VM -Name $VMName;

Once the VM started and you are logged on to VMM01 via Hyper-V remote console configure MGMT interface with PowerShell:

# Configure MGMT interface

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 10.0.0.225 `

–PrefixLength 24 `

-DefaultGateway 10.0.0.1;

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 10.0.0.1;

Set time zone on VMM01:

# List Time Zones

tzutil /l | more

# Set Time Zone

tzutil /s "<your time zone>"

Rename the server and join it to domain:

# Join VMM01 to domain

# Join computer to domain and restart

Add-Computer -DomainName "dms.int" `

-Force `

-Restart `

-NewName "VMM01";

To format the additional disk on VMM01 execute the code below on the DC01:

Invoke-Command -ComputerName VMM01 -ScriptBlock {

$RawDisks = Get-Disk | where partitionstyle -eq 'raw'| Sort-Object Number;

foreach ($disk in $RawDisks)

{

$InitializedDisk = Initialize-Disk -PartitionStyle GPT `

-Number $disk.Number `

-ErrorAction Stop;

$FormatedVol = New-Partition -DiskNumber $Disk.Number `

-UseMaximumSize `

-DriveLetter T `

-ErrorAction Stop | `

Format-Volume -FileSystem NTFS `

-AllocationUnitSize 65536 `

-NewFileSystemLabel Data `

-Force `

-Confirm:$false `

-ErrorAction Stop;

};

};

To be able to logon on the server via Remote Desktop we need to enable it. Execute the commands below on DC01 to enable RDP:

Invoke-Command –Computername VMM01 `

–ScriptBlock {

Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" `

-Name "fDenyTSConnections" `

–Value 0;

Enable-NetFirewallRule -DisplayGroup "Remote Desktop";

};

Install SQL Server 2016 on VMM

VMM requires SQL server for hosting its database. In this step, we will install SQL Server 2016 on the same server where VMM will be installed.

In a production environment, it is recommended to place the SQL server for VMM on a separate server with one of the SQL high availability options.

On the DC01 start Active Directory Users and Computers console. Create a new user named _SQLService:

Enter password and check 'User cannot change password' and 'Password never expires' options.

Finish the wizard to create the account.

On the DC01 start Active Directory Users and Computers console. Create a new group named SQLAdmins:

Click OK.

Login with RDP to VMM01 and start SQL Server 2016 setup.

Select Installation and then New SQL Server stand-alone installation or add features to an existing installation.

On Product Key page select Evaluation and click Next.

Accept License Terms and click Next.

On Microsoft Update page click Next.

On Install Rules page click Next if all rules are passed and only Windows Firewall is warning.

On Feature Selection page select Database Engine Services. Change Instance root directory to T:\Program Files\Microsoft SQL Server\ , Shared feature directory to T:\Program Files\Microsoft SQL Server\ and Shared feature directory (x86) to T:\Program Files (x86)\Microsoft SQL Server\. Click Next.

On Instance Configuration page select Named instance and enter VMMDB. Click Next.

On Server configuration page configure _SQLService account for SQL Server Agent and SQL Server Database Engine and enter password for both. Set SQL Server Agent service to Automatic. Click Next.

On Database Engine Configuration page add current user and domain group SQLAdmins as SQL Server administrators. Click Next.

On Ready to Install page click Install.

When installation succeeds click Close.

Start SQL Server configuration manager from Start menu:

Navigate to SQL Server Network Configuration - > Protocols for VMMDB -> TCP/IP Properties.

Click on IP Addresses tab. Scroll down to IPAll. Clear the value for TCP Dynamic Ports and enter value 1433 for TCP Port. Click OK.

Click on SQL Server Services and restart SQL Server (VMMDB) service.

Install Virtual Machine Manager 2016

Download Windows ADK (1607) from here and start the setup. It is recommended to download the ADK from a computer where there is Internet access and copy the setup to VMM01. On Specify location page click Next.

On Windows Kits Privacy page click Next.

Accept License Agreement.

Select only Deployment Tools and Windows Preinstallation Environment (Windows PE) and click Install.

When installation completes click Close.

Download Microsoft ODBC Driver 11 for SQL Server from here and start the setup.

Accept License terms and click Next until installation completes.

Download SQL Server 2014 Command Line Utilities from here and start the setup.

Accept License terms and click Next until installation completes.

On the DC01 start Active Directory Users and Computers console. Create a new user named _VMMService:

Enter password and check 'User cannot change password' and 'Password never expires' options.

Finish the wizard to create the account.

Execute the code below on the VMM server to add the VMM Service account to local administrators group.

# Add VMM service account to local administrators group

Net localgroup Administrators "dms\_VMMService" /add

Extract VMM Installation files to C:\System Center 2016 Virtual Machine Manager.

Start VMM 2016 Installation. Click Install.

On Select features to install screen select VMM Management server. Click Next.

On Product Registration information click Next.

Accept License terms and click Next.

On Diagnostics and Usage Data screen click Next.

On Microsoft Update page select Off and click Next.

On Installation Location screen click Next.

On Prerequisites screen click Next if there are no blocking prerequisites.

On Database Configuration screen enter port 1433 and click Next.

On Configure service account and distributed key management select domain account option and enter the credentials for _VMMService account. Click Next.

In a production environment, it is highly recommended to use Active Directory as Distributed Key Management.

On Port configuration screen click Next.

On Library Configuration screen change the share location to T:\Library. Click Next.

On Installation Summary screen click Install.

After the installation is successful click Close.

Download and extract the latest Update Rollup for Virtual Machine Manager 2016. Check the corresponding knowledge base article for additional details on the installation.

Update VMM server with the following command:

# Update VMM Server

msiexec.exe /update <VMM server packagename>

# Update VMM consple

msiexec.exe /update <VMM console packagename>

If there are also hotfixes available after the latest UR please also install them as well.

On the VMM server start the VMM Console and connect.

Click on Settings Pane - > General -> Network Settings and uncheck Create logical networks automatically option. Click Finish.

Create and configure Remote Server

Execute the script below on the Hyper-V host to create Remote VM. When prompted enter the information needed for VM creation.

# VM name

$VMName = "Remote";

# Storage Path

$StoragePath = Read-Host `

-Prompt 'Enter path to storage where VMs will be stored (example C:\ClusterStorage\Volume1) ';

# OS VHDs location

$osVHDPath = Read-Host `

-Prompt 'Enter syspreped VHDs path (Windows Server 2016 DataCenter (with Desktop Experience) (example C:\ClusterStorage\Volume1\WS2016DC-DE.vhdx) ';

$OSVHDname = $osVHDPath.Split("\")[-1]

# Create VM

New-VM -Name $VMName `

-Path "$StoragePath\" `

-Generation 2 `

-SwitchName "Private" `

-MemoryStartupBytes 2GB ;

# Set Proc number

Set-VM -Name $VMName `

-ProcessorCount 2;

# Copy OS VHD

Copy-Item -Path $osVHDPath `

-Destination "$StoragePath\$VMName\" `

-Force;

# Add OS vhd to VM

Add-VMHardDiskDrive -VMName $VMName `

-Path "$StoragePath\$VMName\$OSVHDname";

# Get OS VHD

$vhd = Get-VMHardDiskDrive -VMName $VMName;

# Set boot order

Set-VMFirmware -VMName $VMName `

-FirstBootDevice $vhd;

# Rename Network adapter

Get-VMNetworkAdapter -VMName $VMName | Rename-VMNetworkAdapter -NewName "Private";

Set-VMNetworkAdapter -Name "Private" `

-VMName $VMName `

-DeviceNaming On;

# Add Public Adapter

Add-VMNetworkAdapter -VMName $VMName `

-SwitchName "Public" `

-Name "Public" `

-DeviceNaming On;

# Start VM

Start-VM -Name $VMName;

Once the VM started and you are logged on to Remote via Hyper-V remote console configure network interfaces with PowerShell:

# Configure Public interface

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "Public"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 50.50.50.1 `

–PrefixLength 24;

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 50.50.50.2;

# Configure Private interface

$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "Private"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration

Remove-NetIPAddress -InterfaceAlias $Interface `

-Confirm:$false ;

# Configure IP settings on interface

New-NetIPAddress –InterfaceAlias $Interface `

–IPAddress 60.60.60.1 `

–PrefixLength 24;

# Set DNS

Set-DnsClientServerAddress -InterfaceAlias $Interface `

-ServerAddresses 60.60.60.1;

Rename the server and restart:

# Rename the server

Rename-Computer -NewName "Remote" `

-Restart;

Set time zone on Remote:

# List Time Zones

tzutil /l | more

# Set Time Zone

tzutil /s "<your time zone>"

On Remote start Server manager and install Remote access role with Routing and DirectAccess and VPN features.

Navigate to C:\inetpub\wwwroot on Remote and edit iisstart.htm in Notepad.

Edit the file so it will look like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

<title>IIS Windows Server</title>

</head>

<body>

Remote

</body>

</html>

Save it.

Start PowerShell as Administrator on Remote and execute the following command:

# Reset IIS

iisreset

Setup Software Defined Data Center with VMM

Virtual Machine Manager is powerful data center management tool. Some of the VMM's major capabilities are:

  • Datacenter - Configure and manage your datacenter components as a single fabric in VMM. Datacenter components include virtualization servers, networking components, and storage resources. VMM provisions and manages the resources needed to create and deploy virtual machines and services to private clouds.
  • Virtualization hosts: - VMM can add, provision, and manage Hyper-V and VMware virtualization hosts and clusters.
  • Networking - Add networking resources to the VMM fabric, including network sites defined by IP subnets, virtual LANs (VLANs), logical switches, static IP address and MAC pools. VMM provides network virtualization, including support for creating and manage virtual networks and network gateways. Network virtualization allows multiple tenants to have isolated networks and their own IP address ranges for increased privacy and security. Using gateways, VMs on virtual networks can connect to physical networks in the same site or in different locations.
  • Storage - VMM can discover, classify, provision, allocate, and assign local and remote storage. VMM supports block storage (fibre channel, iSCSI, and Serial Attached SCSI (SAS) storage area networks (SANs)).
  • Library resources - The VMM fabric retains a library of file-based and non file-based resources that are used to create and deploy VMs and services on virtualization hosts. File-based resources include virtual hard disks, ISO images, and scripts. Non file-based resources include templates and profiles that are used to standardize the creation of VMs. Library resources are accessed through library shares.

In the next section, we will deploy, configure and put under VMM management all the main components for Software Defined Data Center.

Setup Software Defined Storage and Compute

VMM can manage and deploy Compute and Storage in both converged and hyper-converged scenarios. In this section, we will add our Storage Spaces Direct Cluster (hyper-converged) under management and configure it in VMM.

VMM is able to do bare-metal deployment on Scale-out File Server (SOFS) clusters and Hyper-V clusters.

Create and Configure Hyper-V hosts Management Account

On the domain controller start Active Directory Users and Computers console. Create a new user named _HyperVHostsAdmin:

Enter password and check 'User cannot change password' and 'Password never expires' options.

Finish the wizard to create the account. This account will be added to VMM to be used for managing the S2D Cluster.

Open the properties of _HyperVHostsAdmin service account and add group S2DAdmins in the member of settings. Click OK.

On the VMM server open VMM Console. Navigate to Settings -> Run As Accounts. From the ribbon menu click on Create Run As Account. For name enter Hyper-V Hosts Admin and in the credentials area enter the credentials for _HyperVHostsAdmin service account. Click Finish to save the settings.

Alternatively, you can use PowerShell and VMM cmdlets to create the run as account by replacing the values with your own.

$GUID = [guid]::NewGuid();

$credential = Get-Credential;

$runAsAccount = New-SCRunAsAccount -Credential $credential `

-Name "Hyper-V Hosts Admin" `

-Description "" `

-JobGroup $GUID.Guid;

Create Management Network

On the VMM server start VMM console. Click on Fabric pane -> Networking -> Logical Networks. Right click on Logical Networks and select Create Logical Network.

Enter Management for Name value and click Next.

In Settings select One Connected Network. All management networks need to have routing and connectivity between all hosts in that network. Select Create a VM network with the same name to allow virtual machines to access this logical network directly to automatically create a VM network for your management network. Click Next.

On Network Site click Add. Check All Hosts. Click Insert row. Insert your management network IP subnet details. If the management network does not have VLAN enter 0. This network should already exist and be configured in your physical switch. Click Next

On Summary page click Finish to create the logical network.

Alternatively, you can use PowerShell and VMM cmdlets to create the logical network by replacing the values with your own.

# Create Logical Network

$logicalNetwork = New-SCLogicalNetwork -Name "Management" `

-LogicalNetworkDefinitionIsolation $false `

-EnableNetworkVirtualization $false `

-UseGRE $false `

-IsPVLAN $false;

$allHostGroups = @()

$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";

$allSubnetVlan = @()

$allSubnetVlan += New-SCSubnetVLan -Subnet "10.0.0.0/24" `

-VLanID 0;

New-SCLogicalNetworkDefinition -Name "Management_0" `

-LogicalNetwork $logicalNetwork `

-VMHostGroup $allHostGroups `

-SubnetVLan $allSubnetVlan `

-RunAsynchronously;

New-SCVMNetwork -Name "Management" `

-IsolationType "NoIsolation" `

-LogicalNetwork $logicalNetwork;

Next, we need to create IP Pool for the Management Network.

Right-click the Management logical network and select Create IP Pool.

On Name page enter Management IP Pool for value and click Next.

On Network Site page keep default settings and click Next.

On IP address range page enter the range of IPs that will be given to Hyper-V hosts and VMs located in All Hosts Group. You can also exclude certain IPs from that range. In the case below the IP for the VMM server is excluded as this server will not be located on those Hyper-V hosts. Click Next to proceed when information is filled.

On Gateway page add default Gateway for the management network. Click Next.

On DNS page enter the IP or IPs for your DNS server/s. Click Next.

On WINS page click Next.

On Summary page click Finish to create the IP pool.

Alternatively, you can use PowerShell and VMM cmdlets to create the IP Pool by replacing the values with your own.

# Create IP Pool

# Get Logical Network 'Management'

$logicalNetwork = Get-SCLogicalNetwork -Name Management;

# Get Logical Network Definition 'Management_0'

$logicalNetworkDefinition = Get-SCLogicalNetworkDefinition -Name Management_0;

# Network Routes

$allNetworkRoutes = @();

# Gateways

$allGateways = @();

$allGateways += New-SCDefaultGateway -IPAddress "10.0.0.1" `

-Automatic;

# DNS servers

$allDnsServer = @("10.0.0.1");

# DNS suffixes

$allDnsSuffixes = @();

# WINS servers

$allWinsServers = @();

New-SCStaticIPAddressPool -Name "Management IP Pool" `

-LogicalNetworkDefinition $logicalNetworkDefinition `

-Subnet "10.0.0.0/24" `

-IPAddressRangeStart "10.0.0.211" `

-IPAddressRangeEnd "10.0.0.240" `

-DefaultGateway $allGateways `

-DNSServer $allDnsServer `

-DNSSuffix "" `

-DNSSearchSuffix $allDnsSuffixes `

-NetworkRoute $allNetworkRoutes `

-RunAsynchronously `

-IPAddressReservedSet "10.0.0.225";

Create Logical Switch

Open VMM Console on the VMM Server. Navigate to Fabric pane -> Networking -> Logical Switches. From the ribbon menu choose Create Logical Switch. On Getting Started Page click Next.

On General page enter Prod for name and choose Embedded Team for Uplink mode. Click Next.

On Settings page leave the default settings and click Next.

On Extensions page clear all extensions and click Next.

On Virtual Port page click Add. For Port classification choose Host Management. Check Include A Hyper-V virtual network adapter port profile in this virtual port. For Hyper-V virtual network adapter port profile choose Host Management. Click OK.

Add more virtual ports by using the table below with the same instructions

Port Classification

Hyper-V virtual network adapter port profile

 

Network Load Balancing

Network Load Balancer NIC Profile

 

Medium Bandwidth

Medium Bandwidth adapter

 

Make Host Management classification default and click Next.

On Uplinks page click on Add button and select New Uplink Port profile. Give ProdUplink name for the profile. Leave Load balancing algorithm and teaming mode to default values. Select Manageemnt_0 network site.

On the Uplinks page select the ProdUplink profile and click on New virtual network adapter. Give Management for Name value. Make sure Management VM Network is selected. Check This virtual network adapter will be used for host management option. Check Inherit connection settings from host network adapter option. Choose Host Management for Classification. Click Next.

Summary page click Finish to create the logical switch.

Alternatively, you can use PowerShell and VMM cmdlets to create logical switch by replacing the values with your own.

$logicalSwitch = New-SCLogicalSwitch -Name "Prod" `

-Description "" `

-EnableSriov $false `

-SwitchUplinkMode "EmbeddedTeam" `

-MinimumBandwidthMode "Weight";

# Get Network Port Classification 'Network load balancing'

$portClassification = Get-SCPortClassification -Name "Network load balancing";

# Get Hyper-V Switch Port Profile 'Network load balancer NIC Profile'

$nativeProfile = Get-SCVirtualNetworkAdapterNativePortProfile -Name "Network load balancer NIC Profile";

New-SCVirtualNetworkAdapterPortProfileSet -Name "Network load balancing" `

-PortClassification $portClassification `

-LogicalSwitch $logicalSwitch `

-RunAsynchronously `

-VirtualNetworkAdapterNativePortProfile $nativeProfile;

# Get Network Port Classification 'Medium bandwidth'

$portClassification = Get-SCPortClassification -Name "Medium bandwidth";

# Get Hyper-V Switch Port Profile 'Medium Bandwidth Adapter'

$nativeProfile = Get-SCVirtualNetworkAdapterNativePortProfile -Name "Medium Bandwidth Adapter";

New-SCVirtualNetworkAdapterPortProfileSet -Name "Medium bandwidth" `

-PortClassification $portClassification `

-LogicalSwitch $logicalSwitch `

-RunAsynchronously `

-VirtualNetworkAdapterNativePortProfile $nativeProfile;

# Get Network Port Classification 'Host management'

$portClassification = Get-SCPortClassification -Name "Host management";

# Get Hyper-V Switch Port Profile 'Host management'

$nativeProfile = Get-SCVirtualNetworkAdapterNativePortProfile -Name "Host management";

New-SCVirtualNetworkAdapterPortProfileSet -Name "Host management" `

-PortClassification $portClassification `

-LogicalSwitch $logicalSwitch `

-RunAsynchronously `

-IsDefaultPortProfileSet $true `

-VirtualNetworkAdapterNativePortProfile $nativeProfile;

$definitions = @()

# Get Logical Network Definition 'Management_0'

$definitions += Get-SCLogicalNetworkDefinition -Name Management_0;

$nativeUppVar = New-SCNativeUplinkPortProfile -Name "ProdUplink" `

-Description "" `

-LogicalNetworkDefinition $definitions `

-EnableNetworkVirtualization $false `

-LBFOLoadBalancingAlgorithm "HostDefault" `

-LBFOTeamMode "SwitchIndependent" `

-RunAsynchronously;

$uppSetVar = New-SCUplinkPortProfileSet -Name "ProdUplink" `

-LogicalSwitch $logicalSwitch `

-NativeUplinkPortProfile $nativeUppVar `

-RunAsynchronously;

# Get VM Network 'Management'

$vmNetwork = Get-SCVMNetwork -Name Management;

# Get Network Port Classification 'Host management'

$vNICPortClassification = Get-SCPortClassification -Name "Host Management";

New-SCLogicalSwitchVirtualNetworkAdapter -Name "Management" `

-UplinkPortProfileSet $uppSetVar `

-RunAsynchronously `

-VMNetwork $vmNetwork `

-VLanEnabled $false `

-PortClassification $vNICPortClassification `

-IsUsedForHostManagement $true `

-InheritsAddressFromPhysicalNetworkAdapter $true `

-IPv4AddressType "Dynamic" `

-IPv6AddressType "Dynamic";

Add Hyper-converged S2D Cluster Under Management

On the VMM server open the VMM console. Navigate to Fabric pane -> Servers - > All Hosts. From the ribbon menu click on Add resources and then Hyper-V Hosts and Clusters. On Resource Location page select Windows Server computers in a trusted Active Directory domain and click Next.

On Credentials page select Use an existing Run As account and click Browse. Choose Hyper-V Hosts Admin and click Next.

On Discovery Scope page type the cluster name S2D-CLU in Computer Names field and click Next.

On Target Resource page when the cluster and its nodes are discovered select it and click Next.

On Host Settings page leave All Hosts as Host Group and click Next.

On Summary page click finish to start adding the cluster under VMM management.

Wait until all jobs are completed. Warning 26179 can safely be ignored for Hyper-Converged S2D Cluster.

Alternatively, you can use PowerShell and VMM cmdlets to add the cluster under management by replacing the values with your own.

$runAsAccount = Get-SCRunAsAccount -Name "Hyper-V Hosts Admin";

# Get Host Group 'All Hosts'

$hostGroup = Get-SCVMHostGroup -Name "All Hosts";

Add-SCVMHostCluster -Name "S2D-CLU.dms.int" `

-RunAsynchronously `

-VMHostGroup $hostGroup `

-Credential $runAsAccount `

-RemoteConnectEnabled $true;

Create Hyper-V Switch on Hyper-V Hosts

On VMM Server start VMM console. Navigate to Fabric pane -> Servers -> All Hosts - > S2D-CLU. Right click on S2D01 and choose Properties.

Select Virtual Switch Tab. Click on New Virtual Switch and choose New Logical Switch. Select Prod for Logical Switch and click OK to save the settings.

Click Yes on the prompted dialog.

Wait until the job completes successfully.

Repeat the same steps for the other nodes of the cluster.

Alternatively, you can use PowerShell and VMM cmdlets to configure logical switch on a Hyper-V node by replacing the values with your own.

# Generate Job GUID

$GUID = [guid]::NewGuid();

# Get Host 'S2D02.dms.int'

$vmHost = Get-SCVMHost -ComputerName S2D02;

# Get Host Network Adapter 'Microsoft Hyper-V Network Adapter'

$networkAdapter = Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `

-VMHost S2D02;

$uplinkPortProfileSet = Get-SCUplinkPortProfileSet -Name ProdUplink;

Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $networkAdapter `

-UplinkPortProfileSet $uplinkPortProfileSet `

-JobGroup $GUID.Guid;

$networkAdapter = @();

$networkAdapter += Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `

-VMHost S2D02;

$logicalSwitch = Get-SCLogicalSwitch -Name Prod;

New-SCVirtualNetwork -VMHost $vmHost `

-VMHostNetworkAdapters $networkAdapter `

-LogicalSwitch $logicalSwitch `

-DeployVirtualNetworkAdapters `

-JobGroup $GUID.Guid;

Set-SCVMHost -VMHost $vmHost `

-JobGroup $GUID.Guid `

-RunAsynchronously;

After all nodes are added restart each S2D node one by one.

Execute the commands below on VMM01 to set cluster reserve to 0 so we can deploy more VMs:

# Set Cluster reserve to 0

$hostCluster = Get-SCVMHostCluster -Name S2D-CLU;

Set-SCVMHostCluster -VMHostCluster $hostCluster `

-ClusterReserve "0";

Add Hyper-Converged S2D Cluster as Storage Provider

When Hyper-Converged S2D cluster is added as Hyper-V cluster to VMM additionally has to be added as storage provider as well in order to manage storage related tasks. Open VMM console on the VMM server. Navigate to Fabric pane -> Storage -> Providers. From ribbon menu choose Add resources and then Storage Devices. From Select Provider Type choose Windows-based file server and click Next.

On Specify Discovery Scope page enter the FQDN of the S2D cluster. For Run As account choose Hyper-V Hosts Admin and click Next.

When S2D cluster is scanned successfully click Next.

On Select Storage Devices page click Next.

On Summary page click Finish to add the storage.

Alternatively, you can use PowerShell and VMM cmdlets to add storage provider by replacing the values with your own.

# Generate Job GUID

$GUID = [guid]::NewGuid();

$runAsAccount = Get-SCRunAsAccount -Name "Hyper-V Hosts Admin";

Add-SCStorageProvider -ComputerName "S2D-CLU.dms.int" `

-AddWindowsNativeWmiProvider `

-Name "S2D-CLU.dms.int" `

-RunAsAccount $runAsAccount `

-RunAsynchronously;

$provider = Get-SCStorageProvider -Name "S2D-CLU";

Set-SCStorageProvider -StorageProvider $provider `

-JobGroup $GUID.Guid `

-RunAsynchronously;

Create and Add Storage Classification

Open VMM console on the VMM server. Navigate to Fabric pane -> Storage -> Classifications and Pools. From ribbon menu click on Create Storage Classification. Enter Gold for the name of the storage Classification and click OK.

Alternatively, you can use PowerShell and VMM cmdlets to create Storage Classification by replacing the values with your own.

New-SCStorageClassification -Name "Gold" `

-Description "" `

-RunAsynchronously;

Navigate to Fabric pane ->Storage -> Arrays. Right click on Clustered Windows Storage on S2D-CLU and choose Properties.

Select Storage Pools tab, against S2D on S2D-CLU choose Gold for Classification and click OK.

Alternatively, you can use PowerShell and VMM cmdlets to assign storage classification to a pool by replacing the values with your own.

$array = Get-SCStorageArray -Name "Clustered Windows Storage on S2D-CLU";

$enabledPools = @();

$enabledPools += Get-SCStoragePool -Name "S2D on S2D-CLU";

$classifications = @();

$classifications += Get-SCStorageClassification -Name Gold;

$hostGroups = @();

$hostGroups += $null;

Set-SCStorageArray -RunAsynchronously `

-StorageArray $array `

-AddStoragePoolToManagement $enabledPools `

-StorageClassificationAssociation $classifications `

-VMHostGroupAssociation $hostGroups;

Create CSV

Open VMM console on the VMM server. Navigate to Fabric pane -> Servers -> All Hosts -> S2D-CLU. Right click on S2D-CLU and choose Properties. Select Shared Volumes tab and click Add.

Click Create Volume. On Storage Type page enter Volume1 for Name and click Next.

On Capacity page enter 500 for Size. Leave File System to ReFS. Select Configure advanced storage and tiering settings. Click Next

On Storage Settings page leave Storage Tier count to 2. Leave Allocation unit size to 4. Configure storage tiers as in the table below. Click Next.

Tier Name

Capacity (GB)

Resiliency

Media Type

Capacity

100

Parity

HDD

Performance

400

Mirror

SSD

On Summary page click Finish to create the volume.

Click Cancel on Add Cluster Shared Volume window. Click Cancel on S2D-CLU properties.

Check the Shared Volume tab in S2D-CLU properties and the volume should be listed there.

Alternatively, you can use PowerShell and VMM cmdlets to create CSV pool by replacing the values with your own.

# Generate Job GUID

$GUID = [guid]::NewGuid();

$storageArray = Get-SCStorageArray -Name "Clustered Windows Storage on S2D-CLU";

$storagePool = Get-SCStoragePool -Name "S2D on S2D-CLU";

New-SCStorageTier -StorageTierFriendlyName "Capacity" `

-StorageTierSizeInMB 102400 `

-RunAsynchronously `

-JobGroup $GUID.Guid;

New-SCStorageTier -StorageTierFriendlyName "Performance" `

-StorageTierSizeInMB 409600 `

-RunAsynchronously `

-JobGroup $GUID.Guid;

New-SCStorageVolume -StorageArray $storageArray `

-StoragePool $storagePool `

-Name "Volume1" `

-RunAsynchronously `

-JobGroup $GUID.Guid `

-FileSystem "CSVFS_ReFS";

Setup Software Defined Networking

Deploy and Setup Network Controller

Copy SDN Setup Files

Copy the contents of VMM to C:\ drive on VMM01.

Add Windows Server 2016 Image to VMM Library

Copy syspreped Windows Server 2016 Datacenter (with Desktop Experience) and latest updates vhdx to T:\Library\VHDs on the VMM Server. Name it WS2016DC_disk_1.vhdx.

Refresh the VMM library from the VMM console.

VHD WS2016DC_disk_1.vhdx will show up in the VMM Library. Enter the properties of the vhd to Windows Server 2016 Datacenter for Operating System and Microsoft Hyper-V for Virtualization Platform. Click OK to save the settings.

Alternatively, you can use PowerShell and VMM cmdlets set vhd properties by replacing the values with your own.

$libraryObject = Get-SCVirtualHardDisk -Name WS2016DC_disk_1.vhdx;

# Get Operating System 'Windows Server 2016 Datacenter'

$os = Get-SCOperatingSystem | where {$_.Name -eq "Windows Server 2016 Datacenter"};

Set-SCVirtualHardDisk -VirtualHardDisk $libraryObject `

-OperatingSystem $os `

-VirtualizationPlatform "HyperV" `

-Name "WS2016DC_disk_1.vhdx" `

-Description "" `

-Release "" `

-FamilyName "";

Generate SSL Certificate for Network Controller

You need an SSL certificate that will be used to establish secure communication (https) between VMM and the network controller. We will generate a self-signed certificate for the deployment. Execute the commands below on VMM01 to generate self-signed certificate:

$NetworkControllerFQDN = "NCE-NCVM01.dms.int"

$ServerCertificatePassword = "!!Password"

$generatedCert = New-SelfSignedCertificate -KeyUsageProperty All `

-Provider "Microsoft Strong cryptographic provider" `

-FriendlyName "NC certificate" `

-DnsName $NetworkControllerFQDN;

$certPassword = ConvertTO-SecureString -String $ServerCertificatePassword `

-Force `

-AsPlainText;

$certPath = "cert:\LocalMachine\My\" + $generatedCert.Thumbprint;

#The File path parameter should be path of downloaded service template servercertificate.cr folder for NC

$Exportedcert = Export-pfxCertificate -Cert $certPath `

-FilePath "C:\VMM\Templates\NC\ServerCertificate.cr\ServerCert.pfx" `

-Password $certPassword;

    

#Export the cert for SLB

$Exportedcert = Export-Certificate -Cert $certPath `

-FilePath "C:\VMM\Templates\SLB\NCCertificate.cr\MCCert.cer";

The certificate is imported in the local machine certificate store and also exported to the service template resources for Network Controller and Software Load Balancer.

Import Network Controller Service Template

In this step, we will import service template into VMM that will be used for deploying Network Controller. Execute the following script in VMM PowerShell on VMM01:

$serviceTemplateLocation = "C:\VMM\Templates\NC\";

$PackagePath = "C:\VMM\Templates\NC\Network Controller Standalone Generation 2 VM.xml";

$VHDName = "WS2016DC_disk_1.vhdx";

#Get the package

$package = Get-SCTemplatePackage -Path $PackagePath;

    

#Get the package mapping

$allMappings = New-SCPackageMapping -TemplatePackage $package;

$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"};

$resource = Get-SCVirtualHardDisk -Name $VHDName;

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

$VMMLibrary = Get-SCLibraryShare;

$NCsetupPath = $serviceTemplateLocation + "NCSetup.cr\";

Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `

-SharePath $VMMLibrary[0] `

-OverwriteExistingFiles;

$mapping = $allMappings | where {$_.PackageId -eq "NCSetup.cr"};

$resource = Get-SCCustomResource -Name "NCSetup.cr";

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

#MAP ServerCertificate.cr

$NCsetupPath = $serviceTemplateLocation + "ServerCertificate.cr\";

Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `

-SharePath $VMMLibrary[0] `

-OverwriteExistingFiles;

$mapping = $allMappings | where {$_.PackageId -eq "ServerCertificate.cr"};

$resource = Get-SCCustomResource -Name "ServerCertificate.cr";

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

#MAP TrustedRootCertificate.cr

$NCsetupPath = $serviceTemplateLocation + "TrustedRootCertificate.cr\";

Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `

-SharePath $VMMLibrary[0] `

-OverwriteExistingFiles;

$mapping = $allMappings | where {$_.PackageId -eq "TrustedRootCertificate.cr"};

$resource = Get-SCCustomResource -Name "TrustedRootCertificate.cr";

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

$serviceTemplate = Import-SCTemplate -TemplatePackage $package `

-Name "NC Deployment service Template" `

-PackageMapping $allMappings `

-Release "1.0" `

-SettingsIncludePrivate;

$Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "NC-VM##"};

$ComputerNamePattern = "NCE-NCVM##";

Set-SCVMTemplate -Template $Template `

-ComputerName $ComputerNamePattern `

-ProductKey $ProductKey;

After successful import the service template is visible in the VMM library:

Note that the template will set the time zone of the deplpyed VMs to Western European Time. If needed edit the template to change the time zone of the VMs before proceeding to deployment.

Create Network Controller Users and Groups

On the domain controller, open Active Directory Users and Computers console and create user account with name _NCService. Click Next.

For password enter Password!! and check both User cannot change password and Password never expires. Click Next.

Click Finish to create the account.

Create a domain local security group with name NCUsers.

Add _NCService account and Administrator account as members of NCUsers:

Create another domain local security group named NCAdmins.

Add _NCService account and Administrator account as members of NCAdmins:

Deploy Network Controller with Service Template

Execute the code below to deploy Network Controller via Service Template on VMM01:

# The following are service settings required for configuring and

# deploying the service template imported client security Group Name

$ClientSecurityGroupName = "dms\NCUsers";

# Local Admin credentials

# The local admin user name will be .\Administrator

$LocalAdminPassword = "Password123!!";

# Management Domain Account Which will be used for NC Deployment

$ManagementDomainUserPassword = "Password!!";

$ManagementDomainUser = "dms\_NCService";

# This is the domain which NC VM will join

$ManagementDomainFDQN = "dms.int";

#Managemet Security Group Name

$ManagementSecurityGroupName = "dms\NCAdmins";

#The password for server certificate

$ServerCertificatePassword = "!!Password";

# Get the host group on which the service is to be deployed

$ServiceHostGroup = Get-SCVMHostGroup -Name "All Hosts";

    

#Get the service template

$serviceTemplate = Get-SCServiceTemplate -Name "NC Deployment service Template";

    

#Create a new service configuration

$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate `

-Name "NC" `

-VMHostGroup $ServiceHostGroup;

$ManagementVMNetwork = Get-SCVMNetwork -Name "Management"

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "Management" | Set-SCServiceSetting -value $ManagementVMNetwork.ID;

#update the service configuration to apply placement. If there is any error,Lets stop

$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig;

if($ServiceUpdate.deploymenterrorlist -ne $null)

{

    Write-Host "Placement failed for Service Deployment";

    exit -1

}

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "ClientSecurityGroup" | Set-SCServiceSetting -value $ClientSecurityGroupName;

# Create the Local Admin Run As Account

$localAdminCredPassword = ConvertTo-SecureString -String $LocalAdminPassword `

-Force `

-AsPlainText;

$localAdminCred = New-Object System.Management.Automation.PSCredential (".\Administrator", $localAdminCredPassword);

$localAdminRAA = New-SCRunAsAccount -Name "NC_LocalAdminRAA" `

-Credential $localAdminCred `

-NoValidation;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "LocalAdmin" | Set-SCServiceSetting -value $localAdminRAA;

# Create the Network Controller Management Run As Account

$MgmtDomainCredPassword = ConvertTo-SecureString -String $ManagementDomainUserPassword `

-Force `

-AsPlainText;

$MgmtDomainCred = New-Object System.Management.Automation.PSCredential ($ManagementDomainUser, $MgmtDomainCredPassword);

$MgmtAdminRAA = New-SCRunAsAccount -Name "NC_MgmtAdminRAA" `

-Credential $MgmtDomainCred;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtDomainAccount" | Set-SCServiceSetting -value $MgmtAdminRAA;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtDomainAccountName" | Set-SCServiceSetting -value $ManagementDomainUser;

$domainpwd = ConvertTo-SecureString -String $ManagementDomainUserPassword `

-Force `

-AsPlainText;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtDomainAccountPassword" | Set-SCServiceSetting -Securevalue $domainpwd;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtDomainFQDN" | Set-SCServiceSetting -value $ManagementDomainFDQN;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtSecurityGroup" | Set-SCServiceSetting -value $ManagementSecurityGroupName;

$certpassword = ConvertTo-SecureString -string $ServerCertificatePassword `

-Force `

-AsPlainText;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "ServerCertificatePassword" | Set-SCServiceSetting -Securevalue $certpassword;

$sc = New-SCService -ServiceConfiguration $ServiceConfig;

After successful deployment Create Service Instance for NC will be successfully completed in VMM jobs.

Connect Network Controller with VMM

Now that the Network Controller is deployed we need to connect it with VMM so it will start to manage SDN. Execute the code below on VMM01 to connect Network Controller to VMM:

# NC Name

$VMName = "NCE-NCVM01.dms.int";

# Get NC Run As Management Account

$runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";

# Get NC configuration provider

$configurationProvider = Get-SCConfigurationProvider -Name "Microsoft Network Controller";

# Get the Host group NC will manage

$vmHostGroup = @()

$vmHostGroup += Get-SCVMHostGroup -Name "All Hosts";

# Get NC certificate

$certificates = @()

$certificates += Get-SCCertificate -ComputerName $VMName `

-TCPPort 443;

# Construct Connection string

$ConnectionString = "serverurl=https://";

$ConnectionString += $VMName;

$ConnectionString += "/;SouthBoundIPAddress=";

$vm = Get-SCVirtualMachine -Name $VMName;

$IPv4Address = $vm.VirtualNetworkAdapters[0].IPv4Addresses;

$ConnectionString += $IPv4Address;

$ConnectionString += ";servicename=NC";

# Add NC to VMM

$NC = Add-SCNetworkService -Name "Network Controller" `

-RunAsAccount $runAsAccount `

-ConfigurationProvider $configurationProvider `

-VMHostGroup $vmHostGroup `

-ConnectionString $ConnectionString `

-Certificate $certificates `

-ProvisionSelfSignedCertificatesForNetworkService $true;

After Network Controller is added Add Network service device job should have all of its sub steps completed successfully.

You will notice that in the properties of the logical switch that is now being controlled by Network Controller.

Create other logical networks for SDN

Virtual Network Functions like Software Load Balancers and RAS Gateways require additional networks in order to be deployed and used. Below are the additional logical networks that needs to be created in VMM.

Logical Network Name

Subnet

Mask

VLAN ID

Gateway

 

Management

10.0.0.0

24

0

10.0.0.1

 

HNVPA

10.10.56.0

24

11

10.10.56.1

 

Transit

10.10.10.0

24

10

10.10.10.1

 

PublicVIP

41.40.40.0

27

NA

41.40.40.1

 

PrivateVIP

20.20.20.0

27

NA

20.20.20.1

 

GREVIP

31.30.30.0

24

NA

31.30.30.1

 

Management is already created in previous step.

The networks in the table have the following usage:

  • Provider logical network - transport network for encapsulating Tenant network traffic.
  • Transit logical network - The RAS Gateway and SLB/MUX use the Transit logical network to exchange BGP peering information and North/South (external-internal) tenant traffic. The size of this subnet will typically be smaller than the others. Only physical compute hosts that run HNV Gateway or SLB/MUX virtual machines need to have connectivity to this subnet with these VLANs trunked and accessible on the switch ports to which the compute hosts' network adapters are connected. Each SLB/MUX or HNV Gateway virtual machine is statically assigned one IP address from the Transit logical network.
  • Public VIP logical network - The Public VIP logical network is required to have IP subnet prefixes that are routable outside of the cloud environment (typically Internet routable). These will be the front-end IP addresses used by external clients to access resources in the virtual networks including the front end VIP for the Site-to-site gateway.
  • Private VIP logical network - The Private VIP logical network is not required to be routable outside of the cloud as it is used for VIPs that are only accessed from internal cloud clients, such as the SLB Manager or private services.
  • GRE VIP logical network - The GRE VIP network is a subnet that exists solely for defining VIPs that are assigned to gateway virtual machines running on your SDN fabric for a S2S GRE connection type. This network does not need to be pre-configured in your physical switches or router and need not have a VLAN assigned.

On VMM01 execute the script below to create HNVPA network:

# Create HNVPA

$LogicalNetworkName = "HNVPA";

$LogicalNetworkSubnet = "10.10.56.0/24";

$LogicalNetworkVlanID = 11;

$LogicalNetworkGateway = "10.10.56.1";

$LogicalNetworkStartIp = "10.10.56.10";

$LogicalNetworkEndIp = "10.10.56.100";

$UplinkProfileName = "ProdUplink";

# Get NC

$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network

$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `

-LogicalNetworkDefinitionIsolation $false `

-EnableNetworkVirtualization $true `

-UseGRE $true `

-IsPVLAN $false `

-NetworkController $NetController;

# Create Network Definition

$allHostGroups = @();

$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";

$allSubnetVLAN = @();

$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `

-VLanID $LogicalNetworkVlanID;

$LNDName = $LogicalNetworkName + "_0";

$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `

-LogicalNetwork $LogicalNetworkCreated `

-VMHostGroup $allHostGroups `

-SubnetVLan $allSubnetVLAN;

# Create IP Pool

$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";

$allDnsServer = @();

$allGateways =@();

$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `

-Automatic;

$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `

-LogicalNetworkDefinition $createdLND `

-Subnet $LogicalNetworkSubnet `

-IPAddressRangeStart $LogicalNetworkStartIp `

-IPAddressRangeEnd $LogicalNetworkEndIp `

-DefaultGateway $allGateways `

-DNSServer $allDnsServer;

# Add Network to Uplink profile

#Get the LogicalNetwork

$LogNet = Get-SCLogicalNetwork -Name $LogicalNetworkName;

# Get the logical Network Definition

$LogicalNetworkDefinition = Get-SCLogicalNetworkDefinition -LogicalNetwork $LogNet;

    

#Get the NC uplink port profile

$uplink = Get-SCNativeUplinkPortProfile -Name $UplinkProfileName;

    

#Set the uplink port profile

Set-SCNativeUplinkPortProfile -NativeUplinkPortProfile $uplink `

-AddLogicalNetworkDefinition $LogicalNetworkDefinition;

After successful creation, the network is visible in VMM.

On VMM01 execute the script below to create Transit network:

# Create Transit Logical Network

$LogicalNetworkName = "Transit";

$LogicalNetworkSubnet = "10.10.10.0/24";

$LogicalNetworkVlanID = 10;

$LogicalNetworkGateway = "10.10.10.1";

$LogicalNetworkStartIp = "10.10.10.10";

$LogicalNetworkEndIp = "10.10.10.100";

$UplinkProfileName = "ProdUplink";

# Get NC

$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network

$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `

-LogicalNetworkDefinitionIsolation $false `

-EnableNetworkVirtualization $false `

-UseGRE $false `

-IsPVLAN $false `

-NetworkController $NetController;

# Create Network Definition

$allHostGroups = @();

$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";

$allSubnetVLAN = @();

$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `

-VLanID $LogicalNetworkVlanID;

$LNDName = $LogicalNetworkName + "_0";

$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `

-LogicalNetwork $LogicalNetworkCreated `

-VMHostGroup $allHostGroups `

-SubnetVLan $allSubnetVLAN;

# Create VM Netowrk

$ManagementVMNetwork = New-SCVMNetwork -Name $LogicalNetworkName `

-IsolationType "NoIsolation" `

-LogicalNetwork $LogicalNetworkCreated;

# Create IP Pool

$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";

$allDnsServer = @();

$allGateways =@();

$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `

-Automatic;

$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `

-LogicalNetworkDefinition $createdLND `

-Subnet $LogicalNetworkSubnet `

-IPAddressRangeStart $LogicalNetworkStartIp `

-IPAddressRangeEnd $LogicalNetworkEndIp `

-DefaultGateway $allGateways `

-DNSServer $allDnsServer;

# Add Network to Uplink profile

#Get the LogicalNetwork

$LogNet = Get-SCLogicalNetwork -Name $LogicalNetworkName;

# Get the logical Network Definition

$LogicalNetworkDefinition = Get-SCLogicalNetworkDefinition -LogicalNetwork $LogNet;

    

#Get the NC uplink port profile

$uplink = Get-SCNativeUplinkPortProfile -Name $UplinkProfileName;

    

#Set the uplink port profile

Set-SCNativeUplinkPortProfile -NativeUplinkPortProfile $uplink `

-AddLogicalNetworkDefinition $LogicalNetworkDefinition;

After successful creation, the network is visible in VMM.

On VMM01 execute the script below to create Private VIP network:

# Create Private VIP Logical Network

$LogicalNetworkName = "PrivateVIP";

$LogicalNetworkSubnet = "20.20.20.0/27";

$LogicalNetworkVlanID = 0;

$LogicalNetworkGateway = "20.20.20.1";

$LogicalNetworkStartIp = "20.20.20.10";

$LogicalNetworkEndIp = "20.20.20.30";

# Get NC

$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network

$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `

-LogicalNetworkDefinitionIsolation $false `

-EnableNetworkVirtualization $false `

-UseGRE $false `

-IsPVLAN $false `

-NetworkController $NetController;

# Create Network Definition

$allHostGroups = @();

$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";

$allSubnetVLAN = @();

$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `

-VLanID $LogicalNetworkVlanID;

$LNDName = $LogicalNetworkName + "_0";

$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `

-LogicalNetwork $LogicalNetworkCreated `

-VMHostGroup $allHostGroups `

-SubnetVLan $allSubnetVLAN;

# Create VM Netowrk

$ManagementVMNetwork = New-SCVMNetwork -Name $LogicalNetworkName `

-IsolationType "NoIsolation" `

-LogicalNetwork $LogicalNetworkCreated;

# Create IP Pool

$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";

$allDnsServer = @();

$allGateways =@();

$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `

-Automatic;

$VIPAddressSet = ""

$VIPAddressSet += $LogicalNetworkStartIp

$VIPAddressSet += "-"

$VIPAddressSet += $LogicalNetworkEndIp

$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `

-LogicalNetworkDefinition $createdLND `

-Subnet $LogicalNetworkSubnet `

-IPAddressRangeStart $LogicalNetworkStartIp `

-IPAddressRangeEnd $LogicalNetworkEndIp `

-DefaultGateway $allGateways `

-DNSServer $allDnsServer `

-VIPAddressSet $VIPAddressSet;

After successful creation, the network is visible in VMM.

On VMM01 execute the script below to create Public VIP network:

# Create Public VIP logical network

$LogicalNetworkName = "PublicVIP";

$LogicalNetworkSubnet = "41.40.40.0/27";

$LogicalNetworkVlanID = 0;

$LogicalNetworkGateway = "41.40.40.1";

$LogicalNetworkStartIp = "41.40.40.10";

$LogicalNetworkEndIp = "41.40.40.30";

# Get NC

$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network

$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `

-LogicalNetworkDefinitionIsolation $false `

-EnableNetworkVirtualization $false `

-UseGRE $false `

-IsPVLAN $false `

-NetworkController $NetController `

-PublicIPNetwork;

# Create Network Definition

$allHostGroups = @();

$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";

$allSubnetVLAN = @();

$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `

-VLanID $LogicalNetworkVlanID;

$LNDName = $LogicalNetworkName + "_0";

$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `

-LogicalNetwork $LogicalNetworkCreated `

-VMHostGroup $allHostGroups `

-SubnetVLan $allSubnetVLAN;

# Create VM Netowrk

$ManagementVMNetwork = New-SCVMNetwork -Name $LogicalNetworkName `

-IsolationType "NoIsolation" `

-LogicalNetwork $LogicalNetworkCreated;

# Create IP Pool

$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";

$allDnsServer = @();

$allGateways =@();

$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `

-Automatic;

$VIPAddressSet = ""

$VIPAddressSet += $LogicalNetworkStartIp

$VIPAddressSet += "-"

$VIPAddressSet += $LogicalNetworkEndIp

$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `

-LogicalNetworkDefinition $createdLND `

-Subnet $LogicalNetworkSubnet `

-IPAddressRangeStart $LogicalNetworkStartIp `

-IPAddressRangeEnd $LogicalNetworkEndIp `

-DefaultGateway $allGateways `

-DNSServer $allDnsServer `

-VIPAddressSet $VIPAddressSet;

After successful creation, the network is visible in VMM.

On VMM01 execute the script below to create GRE VIP network:

# Create GREVIP

$LogicalNetworkName = "GREVIP";

$LogicalNetworkSubnet = "31.30.30.0/24";

$LogicalNetworkVlanID = 0;

$LogicalNetworkGateway = "31.30.30.1";

$LogicalNetworkStartIp = "31.30.30.10";

$LogicalNetworkEndIp = "31.30.30.100";

# Get NC

$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network

$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `

-LogicalNetworkDefinitionIsolation $false `

-EnableNetworkVirtualization $false `

-UseGRE $false `

-IsPVLAN $false `

-NetworkController $NetController;

# Create Network Definition

$allHostGroups = @();

$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";

$allSubnetVLAN = @();

$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `

-VLanID $LogicalNetworkVlanID;

$LNDName = $LogicalNetworkName + "_0";

$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `

-LogicalNetwork $LogicalNetworkCreated `

-VMHostGroup $allHostGroups `

-SubnetVLan $allSubnetVLAN;

# Create VM Netowrk

$ManagementVMNetwork = New-SCVMNetwork -Name $LogicalNetworkName `

-IsolationType "NoIsolation" `

-LogicalNetwork $LogicalNetworkCreated;

# Create IP Pool

$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";

$allDnsServer = @();

$allGateways =@();

$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `

-Automatic;

$VIPAddressSet = ""

$VIPAddressSet += $LogicalNetworkStartIp

$VIPAddressSet += "-"

$VIPAddressSet += $LogicalNetworkEndIp

$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `

-LogicalNetworkDefinition $createdLND `

-Subnet $LogicalNetworkSubnet `

-IPAddressRangeStart $LogicalNetworkStartIp `

-IPAddressRangeEnd $LogicalNetworkEndIp `

-DefaultGateway $allGateways `

-DNSServer $allDnsServer `

-VIPAddressSet $VIPAddressSet;

After successful creation, the network is visible in VMM.

Deploy and Setup Software Load Balancer

Import Software Load Balancer Service Template

In this step, we will import service template into VMM that will be used for deploying Sofwtare Load Balancers. Execute the following script in VMM PowerShell on VMM01:

$serviceTemplateLocation = "C:\VMM\Templates\SLB\";

$PackagePath = "C:\VMM\Templates\SLB\SLB Production Generation 2 VM.xml";

$VHDName = "WS2016DC_disk_1.vhdx";

$ProductKey="";

    

#Get the package

$package = Get-SCTemplatePackage -Path $PackagePath;

    

#Get the package mapping

$allMappings = New-SCPackageMapping -TemplatePackage $package;

    

#start mapping the resources

    

#MAP the VHD

$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"};

$resource = Get-SCVirtualHardDisk -Name $VHDName;

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

    

#MAP NCsetup.cr

$VMMLibrary = Get-SCLibraryShare;

$NCsetupPath = $serviceTemplateLocation + "\NCCertificate.cr\";

Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `

-SharePath $VMMLibrary[0] `

-OverwriteExistingFiles;

    

$mapping = $allMappings | where {$_.PackageId -eq "NCCertificate.cr"};

$resource = Get-SCCustomResource -Name "NCCertificate.cr";

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

    

#MAP ServerCertificate.cr

$NCsetupPath = $serviceTemplateLocation + "\EdgeDeployment.cr\";

Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `

-SharePath $VMMLibrary[0] `

-OverwriteExistingFiles;

$mapping = $allMappings | where {$_.PackageId -eq "EdgeDeployment.cr"};

$resource = Get-SCCustomResource -Name "EdgeDeployment.cr";

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

    

#Import the service TemplatePackage

$serviceTemplate = Import-SCTemplate -TemplatePackage $package `

-Name "SLB Deployment service Template" `

-PackageMapping $allMappings `

-Release "1.0" `

-SettingsIncludePrivate;

    

$Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "muxvm###"};

$ComputerNamePattern = "NCE-muxvm##";

Set-SCVMTemplate -Template $Template `

-ComputerName $ComputerNamePattern `

-ProductKey $ProductKey;

After successful import the service template is visible in the VMM library:

Note that the template will set the time zone of the deplpyed VMs to Western European Time. If needed edit the template to change the time zone of the VMs before proceeding to deployment.

Deploy Software Load Balancer with Service Template

Execute the code below to deploy Sofwtare Load Balancer via Service Template on VMM01:

$ManagementDomainFDQN = "dms.int";    

# Get the host group on which the service is to be deployed

$ServiceHostGroup = Get-SCVMHostGroup -Name "All Hosts";

    

#Get the service template

$serviceTemplate = Get-SCServiceTemplate -Name "SLB Deployment service Template";

#Resolve the service Template

Resolve-SCServiceTemplate -ServiceTemplate $serviceTemplate `

-update;

    

#Create a new service configuration

$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate `

-Name "Software Load Balancer" `

-VMHostGroup $ServiceHostGroup;

# Set Management Network    

$ManagementNetwork = Get-SCVMNetwork -Name "Management";

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "ManagementNetwork" | Set-SCServiceSetting -value $ManagementNetwork.ID;

# Set Transit Network

$TransitNetwork = Get-SCVMNetwork -Name "Transit";

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "TransitNetwork" | Set-SCServiceSetting -value $TransitNetwork.ID;

#update the service configuration to apply placement. If there is any error,Lets stop

$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig;

if($ServiceUpdate.deploymenterrorlist -ne $null)

{

Write-Host "Placement failed for Service Deployment";

    exit -1

};

    

#set the service template settings

# Create the Local Admin Run As Account

$localAdminRAA = Get-SCRunAsAccount -Name "NC_LocalAdminRAA" ;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "LocalAdmin" |Set-SCServiceSetting -value $localAdminRAA;

# Create the Local Admin Run As Account

$MgmtAdminRAA = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtDomainAccount" | Set-SCServiceSetting -value $MgmtAdminRAA;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtDomainFQDN" | Set-SCServiceSetting -value $ManagementDomainFDQN;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "SelfSignedConfiguration" | Set-SCServiceSetting -value $true;

    

#create Instance of the service

$sc= New-SCService -ServiceConfiguration $ServiceConfig;

After successful deployment Create Service Instance for Software Load Balancer will be successfully completed in VMM jobs.

Connect SLB to Network Controller

Now that the Software Load Balancer is deployed we need to connect it with VMM under the Network Controller so it can be used. Execute the code below on VMM01 to connect it to Network Controller in VMM:

$networkService = Get-SCNetworkService -Name "Network Controller";

    

$fabricRole = Get-SCFabricRole -NetworkService $networkService | where {$_.RoleType -eq "LoadBalancer"};

    

#get the last IP address of Private VIP

$ippool = Get-SCStaticIPAddressPool -Name "PrivateVIP_IPAddressPool_0";

$LBManagerIPAddress = $ippool.IPAddressRangeEnd;

    

$vipPools = @();

$vipPools += Get-SCStaticIPAddressPool -Name "PrivateVIP_IPAddressPool_0";

$vipPools += Get-SCStaticIPAddressPool -Name "PublicVIP_IPAddressPool_0";

$natIPExemptions = @();

    

$fabricRoleConfiguration = New-SCLoadBalancerRoleConfiguration -LBManagerIPAddress $LBManagerIPAddress `

-NatIPExemptions $natIPExemptions `

-VipPools $vipPools;

    

$fabricRole = Set-SCFabricRole -FabricRole $fabricRole `

-LoadBalancerConfiguration $fabricRoleConfiguration;

    

# Get Service Instance 'SLB'

$service = Get-SCService -Name "Software Load Balancer";

# Get RunAs Account 'NC_MgmtAdminRAA'

$runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";

Add-SCFabricRoleResource -FabricRole $fabricRole `

-ServiceInstance $service `

-RunAsAccount $runAsAccount;

Once the Software Load Balancer is added it is visible in the properties of the Nework Controller under Service tab in VMM console.

Additionally, BGP needs to be configured for every SLB instance:

# Add BGP Settings

# Mux 1

$LoadBalancerName = "NCE-muxvm01.dms.int";

# Get Network Service

$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Load Balancer

$fabricRole = Get-SCFabricRole -Type LoadBalancer `

-NetworkService $networkService;

# Configure BGP

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq $LoadBalancerName };

$bgpPeers = @()

$bgpPeers += New-SCNCBGPPeer -RouterName "BGP Peer" `

-RouterIPAddress "10.10.10.1" `

-RouterAsn 64623;

$bgpRouter = New-SCNCBGPRouter -LocalASN 64628 `

-RouterPeers $bgpPeers;

Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `

-NCBGPRouter $bgpRouter;

# Mux 2

$LoadBalancerName = "NCE-muxvm02.dms.int";

# Get Network Service

$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Load Balancer

$fabricRole = Get-SCFabricRole -Type LoadBalancer `

-NetworkService $networkService;

# Configure BGP

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq $LoadBalancerName };

$bgpPeers = @()

$bgpPeers += New-SCNCBGPPeer -RouterName "BGP Peer" `

-RouterIPAddress "10.10.10.1" `

-RouterAsn 64623;

$bgpRouter = New-SCNCBGPRouter -LocalASN 64628 `

-RouterPeers $bgpPeers;

Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `

-NCBGPRouter $bgpRouter;

# Mux 3

$LoadBalancerName = "NCE-muxvm02.dms.int";

# Get Network Service

$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Load Balancer

$fabricRole = Get-SCFabricRole -Type LoadBalancer `

-NetworkService $networkService;

# Configure BGP

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq $LoadBalancerName };

$bgpPeers = @()

$bgpPeers += New-SCNCBGPPeer -RouterName "BGP Peer" `

-RouterIPAddress "10.10.10.1" `

-RouterAsn 64623;

$bgpRouter = New-SCNCBGPRouter -LocalASN 64628 `

-RouterPeers $bgpPeers;

Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `

-NCBGPRouter $bgpRouter;

Create and configure BGP Router Setting for SLB on RRAS VM

As DC01 is acting as router in our environment we need to configure it as BGP router. Select the SLB VM instance and take a note on the IP given to each VM from the Transit network.

Next login to DC01 and execute the code below to configure DC01 as bgp router and add the SLB instance as BGP peer.

# Create BGP Router

Add-BgpRouter -BgpIdentifier 10.10.10.1 `

-LocalASN 64623;

# Add BGP Peers

$Mux001IP = "10.10.10.10"

Add-BgpPeer -Name Mux001 `

-LocalIPAddress 10.10.10.1 `

-PeerIPAddress $Mux001IP `

-LocalASN 64623 `

-PeerASN 64628 `

-OperationMode Mixed `

-PeeringMode Automatic;

$Mux002IP = "10.10.10.11"

Add-BgpPeer -Name Mux002 `

-LocalIPAddress 10.10.10.1 `

-PeerIPAddress $Mux002IP `

-LocalASN 64623 `

-PeerASN 64628 `

-OperationMode Mixed `

-PeeringMode Automatic;

$Mux003IP = "10.10.10.12"

Add-BgpPeer -Name Mux003 `

-LocalIPAddress 10.10.10.1 `

-PeerIPAddress $Mux003IP `

-LocalASN 64623 `

-PeerASN 64628 `

-OperationMode Mixed `

-PeeringMode Automatic;

After the configuration is added exeucute the command below to get the existing confiuguraiton of BGP peers and their connectivity status.

Get-BgpPeer;

It may take a couple of minutes until the status is changed from Connecting to Connected.

Deploy and Setup RAS Gateway

Import RAS Gateway Service Template

In this step, we will import service template into VMM that will be used for deploying RAS Gateway. Execute the following script in VMM PowerShell on VMM01:

$serviceTemplateLocation = "C:\VMM\Templates\GW\";

$PackagePath = "C:\VMM\Templates\GW\EdgeServiceTemplate_Generation2.xml";

$VHDName = "WS2016DC_disk_1.vhdx";

$ProductKey="";

    

#Get the package

$package = Get-SCTemplatePackage -Path $PackagePath;

    

#Get the package mapping

$allMappings = New-SCPackageMapping -TemplatePackage $package;

    

#start mapping the resources

    

#MAP the VHD

$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"};

$resource = Get-SCVirtualHardDisk -Name $VHDName;

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

    

$mapping = $allMappings | where {$_.PackageId -eq "NCCertificate.cr"};

$resource = Get-SCCustomResource -Name "NCCertificate.cr";

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

$mapping = $allMappings | where {$_.PackageId -eq "EdgeDeployment.cr"};

$resource = Get-SCCustomResource -Name "EdgeDeployment.cr";

Set-SCPackageMapping -PackageMapping $mapping `

-TargetObject $resource;

    

#Import the service TemplatePackage

$serviceTemplate = Import-SCTemplate -TemplatePackage $package `

-Name "Gateway Deployment service Template" `

-PackageMapping $allMappings `

-Release "1.0" `

-SettingsIncludePrivate;

    

$Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "GW-VM###"};

$ComputerNamePattern = "NCE-GW-VM##";

Set-SCVMTemplate -Template $Template `

-ComputerName $ComputerNamePattern `

-ProductKey $ProductKey;

After successful import the service template is visible in the VMM library:

Note that the template will set the time zone of the deplpyed VMs to Western European Time. If needed edit the template to change the time zone of the VMs before proceeding to deployment.

Deploy RAS Gateway with Service Template

Execute the code below to deploy RAS Gateway via Service Template on VMM01:

$ManagementDomainFDQN = "dms.int";

# Get the host group on which the service is to be deployed

$ServiceHostGroup = Get-SCVMHostGroup -Name "All Hosts";

    

#Get the service template

$serviceTemplate = Get-SCServiceTemplate -Name "Gateway Deployment service Template";

#Resolve the service Template

Resolve-SCServiceTemplate -ServiceTemplate $serviceTemplate `

-update;

#Create a new service configuration

$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate `

-Name "Gateway Manager" `

-VMHostGroup $ServiceHostGroup;

    

# Set Management Network

$ManagementNetwork = Get-SCVMNetwork -Name "Management";

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "ManagementNetwork" | Set-SCServiceSetting -value $ManagementNetwork.ID;

#update the service configuration to apply placement. If there is any error,Lets stop

$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig;

if($ServiceUpdate.deploymenterrorlist -ne $null)

{

    Write-Host "Placement failed for Service Deployment";

    exit -1

};

    

#set the service template settings

# Create the Local Admin Run As Account

$localAdminRAA = Get-SCRunAsAccount -Name "NC_LocalAdminRAA";

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "AdminAccount" | Set-SCServiceSetting -value $localAdminRAA;

# Create the Local Admin Run As Account

$MgmtAdminRAA = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtDomainAccount" | Set-SCServiceSetting -value $MgmtAdminRAA;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "MgmtDomainFQDN" |Set-SCServiceSetting -value $ManagementDomainFDQN;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `

-Name "SelfSignedConfiguration" | Set-SCServiceSetting -value $true;

    

#create Instance of the service

$sc= New-SCService -ServiceConfiguration $ServiceConfig;

After successful deployment Create Service Instance for Gateway Manager will be successfully completed in VMM jobs.

Add _NCService account to local administrator group on Gateways by using PowerShell session from VMM01:# Add _NCService account to local Administrators

Enter-PSSession nce-gw-vm01

Net localgroup Administrators "dms\_NCService" /add

exit

Enter-PSSession nce-gw-vm02

Net localgroup Administrators "dms\_NCService" /add

exit

Enter-PSSession nce-gw-vm03

Net localgroup Administrators "dms\_NCService" /add

exit

Connect RAS Gateway to Network Controller

Now that the RAS Gateway is deployed we need to connect it with VMM under the Network Controller so it can be used. Execute the code below on VMM01 to connect it to Network Controller in VMM:

$networkService = Get-SCNetworkService -Name "Network Controller";

    

$fabricRole = Get-SCFabricRole -NetworkService $networkService | where {$_.RoleType -eq "Gateway"};

    

#get the last IP address of Private VIP

$GREVIP = get-SCLogicalNetworkDefinition -Name "GREVIP_0";

$subnetVlansGreVip = @();

$subnetVlanGreVipIPv4 = New-SCSubnetVLan -Subnet $GREVIP[0].SubnetVLans[0].Subnet `

-VLanID $GREVIP[0].SubnetVLans[0].VLanID;

$subnetVlansGreVip += $subnetVlanGreVipIPv4;

$ippool = Get-SCStaticIPAddressPool -Name "PublicVIP_IPAddressPool_0";

$publicIPV4Address = Grant-SCIPAddress -PublicIPAddress `

-NetworkController $networkController `

-IPAddress $ippool.IPAddressRangeEnd;

$publicIPAddresses = @();

$publicIPAddresses += $publicIPV4Address;

$fabricRoleConfiguration = New-SCGatewayRoleConfiguration -GatewayCapacityKbps 1024000 `

-PublicIPAddresses $publicIPAddresses `

-RedundantResourceCount 0 `

-GreVipSubnets $subnetVlansGreVip;

$fabricRole = Set-SCFabricRole -FabricRole $fabricRole `

-GatewayConfiguration $fabricRoleConfiguration;

# Get Service Instance 'SLB'

$service = Get-SCService -Name "Gateway Manager";

# Get RunAs Account 'NC_MgmtAdminRAA'

$runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";

$compTier = Get-SCComputerTier -Service $service;

    

$Transit = get-SCLogicalNetworkDefinition -Name "Transit_0";

$subnetVlanIPv4 = New-SCSubnetVLan -Subnet $Transit.SubnetVLans[0].Subnet `

-VLanID $Transit.SubnetVLans[0].VLanID;

    

foreach ($VM in $compTier.VMs)

{

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Resource -eq $VM };

    Add-SCFabricRoleResource -FabricRole $fabricRole `

-VirtualMachine $VM `

-IPv4Subnet $subnetVlanIPv4 `

-RunAsAccount $runAsAccount;

}    

Once the RAS Gateway is added it is visible in the properties of the Nework Controller under Service tab in VMM console.

Additionally, BGP needs to be configured for every RAS Gateway instance:

# Add BGP

# GW1

$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Gateway

$fabricRole = Get-SCFabricRole -Type Gateway `

-NetworkService $networkService;

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq "NCE-GW-VM01.dms.int" };

$bgpPeers = @();

$bgpPeers += New-SCNCBGPPeer -RouterName "gatewaypeer" `

-RouterIPAddress "10.10.10.1" `

-RouterAsn 64623;

$bgpRouter = New-SCNCBGPRouter -LocalASN 65000 `

-RouterPeers $bgpPeers;

Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `

-NCBGPRouter $bgpRouter;

# GW2

$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Gateway

$fabricRole = Get-SCFabricRole -Type Gateway `

-NetworkService $networkService;

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq "NCE-GW-VM02.dms.int" };

$bgpPeers = @();

$bgpPeers += New-SCNCBGPPeer -RouterName "gatewaypeer" `

-RouterIPAddress "10.10.10.1" `

-RouterAsn 64623;

$bgpRouter = New-SCNCBGPRouter -LocalASN 65000 `

-RouterPeers $bgpPeers;

Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `

-NCBGPRouter $bgpRouter;

# GW3

$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Gateway

$fabricRole = Get-SCFabricRole -Type Gateway `

-NetworkService $networkService;

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq "NCE-GW-VM03.dms.int" };

$bgpPeers = @();

$bgpPeers += New-SCNCBGPPeer -RouterName "gatewaypeer" `

-RouterIPAddress "10.10.10.1" `

-RouterAsn 64623;

$bgpRouter = New-SCNCBGPRouter -LocalASN 65000 `

-RouterPeers $bgpPeers;

Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `

-NCBGPRouter $bgpRouter;

Configure BGP Router Setting for RAS Gateway on RRAS VM

As DC01 is acting as router in our environment we need to configure the RAS Gateway instances as BGP peers on it. Select the RAS Gateway VM instance and take a note on the IP given to each VM from the Transit network.

Next login to DC01 and execute the code below to add the RAS Gateway instance as BGP peer.

$GW001IP = "10.10.10.14"

Add-BgpPeer -Name GW001 `

-LocalIPAddress 10.10.10.1 `

-PeerIPAddress $GW001IP `

-LocalASN 64623 `

-PeerASN 65000 `

-OperationMode Mixed `

-PeeringMode Automatic;

$GW002IP = "10.10.10.13"

Add-BgpPeer -Name GW002 `

-LocalIPAddress 10.10.10.1 `

-PeerIPAddress $GW002IP `

-LocalASN 64623 `

-PeerASN 65000 `

-OperationMode Mixed `

-PeeringMode Automatic;

$GW003IP = "10.10.10.15"

Add-BgpPeer -Name GW003 `

-LocalIPAddress 10.10.10.1 `

-PeerIPAddress $GW003IP `

-LocalASN 64623 `

-PeerASN 65000 `

-OperationMode Mixed `

-PeeringMode Automatic;

Get the connectivity status with the command below to make sure it will change to connected.

Get-BgpPeer;

Setup Software Defined Security (Damian)

<To be added>

Manage Software Define Datacenter with VMM

In this section, we will perform some SDDC operations that may be part of day to day tasks for the fabric administrator.

Create and Test Virtual Networks

Create Virtual Network

SDN provides the ability of creating multiple Virtual Networks on top of a single physical network (HNVPA). Execute the script below on VMM01 to create Tenant VM Network with one subnet and one IP pool:

# Get HNVPA logical Network

$logicalNetwork = Get-SCLogicalNetwork -Name "HNVPA";

# Create new Network

$vmNetwork = New-SCVMNetwork -Name "TenantA" `

-LogicalNetwork $logicalNetwork `

-IsolationType "WindowsNetworkVirtualization" `

-CAIPAddressPoolType "IPV4" `

-PAIPAddressPoolType "IPV4";

Write-Output $vmNetwork;

# Create Subnet for the Network

$subnet = New-SCSubnetVLan -Subnet "192.168.1.0/24";

New-SCVMSubnet -Name "Subnet1" `

-VMNetwork $vmNetwork `

-SubnetVLan $subnet;

# Get VMSubnet 'Subnet1'

$vmSubnet = Get-SCVMSubnet -Name "Subnet1" `

-VMNetwork $vmNetwork;

# Create IP Pool for the network

# Gateways

$allGateways = @();

# DNS servers

$allDnsServer = @();

# DNS suffixes

$allDnsSuffixes = @();

# WINS servers

$allWinsServers = @();

New-SCStaticIPAddressPool -Name "Pool1" `

-VMSubnet $vmSubnet `

-Subnet "192.168.1.0/24" `

-IPAddressRangeStart "192.168.1.4" `

-IPAddressRangeEnd "192.168.1.254" `

-DefaultGateway $allGateways `

-DNSServer $allDnsServer `

-DNSSuffix "" `

-DNSSearchSuffix $allDnsSuffixes;

After successful creation, the VM network is visible in VMM console -> VM and Service pane - > VM Networks:

Create VM01

Create a VM with name VM01 in TenantA network:

# Get OS VHD

$VirtualHardDisk = Get-SCVirtualHardDisk -Name "WS2016DC_disk_1.vhdx";

# Create Virtual Disk Drive

New-SCVirtualDiskDrive -SCSI `

-Bus 0 `

-LUN 0 `

-JobGroup $GUIDJob2.Guid `

-CreateDiffDisk $false `

-VirtualHardDisk $VirtualHardDisk `

-FileName "VM01_WS2016DC_disk_1.vhdx" `

-VolumeType BootAndSystem ;

# Get HW profile

$HardwareProfile = Get-SCHardwareProfile | where {$_.Name -eq $HWProfileName }

# Create new VM Template

New-SCVMTemplate -Name $SCTemplateName `

-Generation 2 `

-HardwareProfile $HardwareProfile `

-JobGroup $GUIDJob2.Guid `

-NoCustomization;

# Get VM Template and VM configuration

$template = Get-SCVMTemplate -All | where { $_.Name -eq $SCTemplateName };

$virtualMachineConfiguration = New-SCVMConfiguration -VMTemplate $template `

-Name "VM01";

Write-Output $virtualMachineConfiguration;

# Get Host

$vmHost = Get-SCVMHost -ComputerName S2D01;

# Set VM configuration

Set-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration `

-VMHost $vmHost;

Update-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration;

Set-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration `

-VMLocation "C:\ClusterStorage\Volume1\" `

-PinVMLocation $true;

$AllNICConfigurations = Get-SCVirtualNetworkAdapterConfiguration -VMConfiguration $virtualMachineConfiguration;

$VHDConfiguration = Get-SCVirtualHardDiskConfiguration -VMConfiguration $virtualMachineConfiguration;

Set-SCVirtualHardDiskConfiguration -VHDConfiguration $VHDConfiguration `

-PinSourceLocation $false `

-PinDestinationLocation $false `

-PinFileName $false `

-StorageQoSPolicy $null `

-DeploymentOption "UseNetwork";

Update-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration;

$operatingSystem = Get-SCOperatingSystem | where { $_.Name -eq "Windows Server 2016 Datacenter" };

# Deploy VM

New-SCVirtualMachine -Name "VM01" `

-VMConfiguration $virtualMachineConfiguration `

-Description "" `

-BlockDynamicOptimization $false `

-StartVM `

-JobGroup $GUIDJob2.Guid `

-ReturnImmediately `

-StartAction "NeverAutoTurnOnVM" `

-StopAction "SaveVM" `

-OperatingSystem $operatingSystem;

Once the VM is deplpyed connect ot it via Console and finish the setup by entering password Password123!! for Administrator.

Create VM02

Create a VM with name VM02 in TenantA network:

# Create Job Guids

$GUIDJob1 = [guid]::NewGuid();

$GUIDJob2 = [guid]::NewGuid();

$HWProfileName = "Profile" + [guid]::NewGuid().Guid

$SCTemplateName = "Temporary Template" + [guid]::NewGuid().Guid

# Create scsi adapter

New-SCVirtualScsiAdapter -JobGroup $GUIDJob1.Guid `

-AdapterID 7 `

-ShareVirtualScsiAdapter $false `

-ScsiControllerType DefaultTypeNoType;

# Create virtual dvd drive

New-SCVirtualDVDDrive -JobGroup $GUIDJob1.Guid `

-Bus 0 `

-LUN 1;

# Get VM Network and Subnet

$VMNetwork = Get-SCVMNetwork -Name "TenantA";

$VMSubnet = Get-SCVMSubnet -Name "Subnet1" `

-VMNetwork $VMNetwork;

# Get Port Classificaiton

$PortClassification = Get-SCPortClassification -Name "Medium bandwidth";

# Create virtual network adapter

New-SCVirtualNetworkAdapter -JobGroup $GUIDJob1.Guid `

-MACAddress "00:00:00:00:00:00" `

-MACAddressType Static `

-Synthetic `

-IPv4AddressType Dynamic `

-IPv6AddressType Dynamic `

-VMSubnet $VMSubnet `

-VMNetwork $VMNetwork `

-PortClassification $PortClassification;

# Get CPU type

$CPUType = Get-SCCPUType | where {$_.Name -eq "3.60 GHz Xeon (2 MB L2 cache)"};

# Create new HW profile

New-SCHardwareProfile -CPUType $CPUType `

-Name $HWProfileName `

-Description "Profile used to create a VM/Template" `

-CPUCount 2 `

-MemoryMB 2048 `

-DynamicMemoryEnabled $false `

-MemoryWeight 5000 `

-CPUExpectedUtilizationPercent 20 `

-DiskIops 0 `

-CPUMaximumPercent 100 `

-CPUReserve 0 `

-NumaIsolationRequired $false `

-NetworkUtilizationMbps 0 `

-CPURelativeWeight 100 `

-HighlyAvailable $true `

-HAVMPriority 2000 `

-DRProtectionRequired $false `

-SecureBootEnabled $true `

-SecureBootTemplate "MicrosoftWindows" `

-CPULimitFunctionality $false `

-CPULimitForMigration $false `

-CheckpointType Production `

-Generation 2 `

-JobGroup $GUIDJob1.Guid;

# Get OS VHD

$VirtualHardDisk = Get-SCVirtualHardDisk -Name "WS2016DC_disk_1.vhdx";

# Create Virtual Disk Drive

New-SCVirtualDiskDrive -SCSI `

-Bus 0 `

-LUN 0 `

-JobGroup $GUIDJob2.Guid `

-CreateDiffDisk $false `

-VirtualHardDisk $VirtualHardDisk `

-FileName "VM01_WS2016DC_disk_1.vhdx" `

-VolumeType BootAndSystem ;

# Get HW profile

$HardwareProfile = Get-SCHardwareProfile | where {$_.Name -eq $HWProfileName }

# Create new VM Template

New-SCVMTemplate -Name $SCTemplateName `

-Generation 2 `

-HardwareProfile $HardwareProfile `

-JobGroup $GUIDJob2.Guid `

-NoCustomization;

# Get VM Template and VM configuration

$template = Get-SCVMTemplate -All | where { $_.Name -eq $SCTemplateName };

$virtualMachineConfiguration = New-SCVMConfiguration -VMTemplate $template `

-Name "VM01";

Write-Output $virtualMachineConfiguration;

# Get Host

$vmHost = Get-SCVMHost -ComputerName S2D02;

# Set VM configuration

Set-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration `

-VMHost $vmHost;

Update-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration;

Set-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration `

-VMLocation "C:\ClusterStorage\Volume1\" `

-PinVMLocation $true;

$AllNICConfigurations = Get-SCVirtualNetworkAdapterConfiguration -VMConfiguration $virtualMachineConfiguration;

$VHDConfiguration = Get-SCVirtualHardDiskConfiguration -VMConfiguration $virtualMachineConfiguration;

Set-SCVirtualHardDiskConfiguration -VHDConfiguration $VHDConfiguration `

-PinSourceLocation $false `

-PinDestinationLocation $false `

-PinFileName $false `

-StorageQoSPolicy $null `

-DeploymentOption "UseNetwork";

Update-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration;

$operatingSystem = Get-SCOperatingSystem | where { $_.Name -eq "Windows Server 2016 Datacenter" };

# Deploy VM

New-SCVirtualMachine -Name "VM02" `

-VMConfiguration $virtualMachineConfiguration `

-Description "" `

-BlockDynamicOptimization $false `

-StartVM `

-JobGroup $GUIDJob2.Guid `

-ReturnImmediately `

-StartAction "NeverAutoTurnOnVM" `

-StopAction "SaveVM" `

-OperatingSystem $operatingSystem;

Once the VM is deplpyed connect ot it via Console and finish the setup by entering password Password123!! for Administrator.

Install Web Server Role on VM01 and VM02

Login to VM01 and VM02 via Remote console and install Web server role on each one of them:

Install-WindowsFeature -Name Web-Server;

Test Network Connecitvity between VM01 and VM02

In VMM console take a note on the IP of VM01:

Login to VM02 via Console start Internet Explorer browser and enter the IP of VM01 in the address bar. A web site should open:

Logon on both VM01 and VM02 and execute the following command to enable ICMP traffic:

New-NetFirewallRule -DisplayName "Allow ICMPv4-In" `

-Protocol ICMPv4;

From VM02 ping the IP of VM01:

ping 192.168.1.8

As you can see the connectivity between the VMs in a virtual network is the same like on a physical network.

Manage Storage QoS Policies for VM Efficiency

In this lab, we will create Aggregated and Dedicated Storage QoS Policies and apply them to VM01 and VM02.

Create Aggregated Storage QoS Policy

Execute the code bellow on VMM01 to create Aggregated Storage QoS Policy with Minimum IOPS 400 and Maximum IOPS 500:

# Get Storage Array

$storageArraysToAdd = @();

$storageArraysToAdd += Get-SCStorageArray -Name "Clustered Windows Storage on S2D-CLU";

# Create Policy

New-SCStorageQoSPolicy -Name "AggregatedPolicy500" `

-Description "" `

-PolicyType "Aggregated" `

-IOPSMinimum "400" `

-IOPSMaximum "500" `

-BandwidthLimitMBPS "0" `

-StorageArray $storageArraysToAdd `

-IOPSNormalizationSizeKB "8";

Once successfully create the policy is visible in VMM console -> Fabric pane -> Storage - > QoS Policies:

Assign Aggregated QoS Policy to VM01 and VM02

To assign Storage QoS Policy with name AggregatedPolicy500 to VM01 and VM02 execute the code below on VMM01:

# Get VM01

$VM = Get-SCVirtualMachine -Name VM01;

# Get VM01 disks

$VirtualDiskDrives = @();

$VirtualDiskDrives += Get-SCVirtualDiskDrive -VM $VM;

# Get Storage QoS Policy

$StorageQoSPolicy = Get-SCStorageQoSPolicy -Name "AggregatedPolicy500";

# Assign Policy for each one of the disks

Foreach ($VirtualDiskDrive in $VirtualDiskDrives)

{

Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `

-StorageQoSPolicy $StorageQoSPolicy;

};

# Get VM02

$VM = Get-SCVirtualMachine -Name VM02;

# Get VM02 disks

$VirtualDiskDrives = @();

$VirtualDiskDrives += Get-SCVirtualDiskDrive -VM $VM;

# Get Storage QoS Policy

$StorageQoSPolicy = Get-SCStorageQoSPolicy -Name "AggregatedPolicy500";

# Assign Policy for each one of the disks

Foreach ($VirtualDiskDrive in $VirtualDiskDrives)

{

Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `

-StorageQoSPolicy $StorageQoSPolicy;

};

When you open the properties of VM01 and VM02 under Hardware Configuration ->Bus Configuration -> SCSI Adapter 0 -> WS2016DC_disk_1.vhdx -> Advanced you can see the policy assigned:

Create Dedicated Storage QoS Policy

Execute the code bellow on VMM01 to create Dedicated Storage QoS Policy with Minimum IOPS 400 and Maximum IOPS 500:

# Get Storage Array

$storageArraysToAdd = @();

$storageArraysToAdd += Get-SCStorageArray -Name "Clustered Windows Storage on S2D-CLU";

# Create Policy

New-SCStorageQoSPolicy -Name "DedicatedPolicy500" `

-Description "" `

-PolicyType "Dedicated" `

-IOPSMinimum "400" `

-IOPSMaximum "500" `

-BandwidthLimitMBPS "0" `

-StorageArray $storageArraysToAdd `

-IOPSNormalizationSizeKB "8";

Once successfully create the policy is visible in VMM console -> Fabric pane -> Storage - > QoS Policies:

Assign Dedicated QoS Policy to VM01 and VM02

To assign Storage QoS Policy with name DedicatedPolicy500 to VM01 and VM02 execute the code below on VMM01:

# Get VM01

$VM = Get-SCVirtualMachine -Name VM01;

# Get VM01 disks

$VirtualDiskDrives = @();

$VirtualDiskDrives += Get-SCVirtualDiskDrive -VM $VM;

# Get Storage QoS Policy

$StorageQoSPolicy = Get-SCStorageQoSPolicy -Name "DedicatedPolicy500";

# Assign Policy for each one of the disks

Foreach ($VirtualDiskDrive in $VirtualDiskDrives)

{

Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `

-StorageQoSPolicy $StorageQoSPolicy;

};

# Get VM02

$VM = Get-SCVirtualMachine -Name VM02;

# Get VM02 disks

$VirtualDiskDrives = @();

$VirtualDiskDrives += Get-SCVirtualDiskDrive -VM $VM;

# Get Storage QoS Policy

$StorageQoSPolicy = Get-SCStorageQoSPolicy -Name "DedicatedPolicy500";

# Assign Policy for each one of the disks

Foreach ($VirtualDiskDrive in $VirtualDiskDrives)

{

Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `

-StorageQoSPolicy $StorageQoSPolicy;

};

As you can see there is no need to remove the old policy you can just apply the new one.

When you open the properties of VM01 and VM02 under Hardware Configuration ->Bus Configuration -> SCSI Adapter 0 -> WS2016DC_disk_1.vhdx -> Advanced you can see the policy assigned:

Remove Storage QoS Policy from VM01 and VM02

Storage QoS Policies are removed the same way they are assigned and the only difference is that IOPSMaximum property is set to 0 on the disks. Execute the code below to remove the Storage QoS policies on the disks of VM01 and VM02:

# Get VM01

$VM = Get-SCVirtualMachine -Name VM01;

# Get VM01 disks

$VirtualDiskDrives = @();

$VirtualDiskDrives += Get-SCVirtualDiskDrive -VM $VM;

# Remove Policy for each one of the disks

Foreach ($VirtualDiskDrive in $VirtualDiskDrives)

{

Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `

-IOPSMaximum 0;

};

# Get VM02

$VM = Get-SCVirtualMachine -Name VM02;

# Get VM02 disks

$VirtualDiskDrives = @();

$VirtualDiskDrives += Get-SCVirtualDiskDrive -VM $VM;

# Remove Policy for each one of the disks

Foreach ($VirtualDiskDrive in $VirtualDiskDrives)

{

Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `

-IOPSMaximum 0;

};

The changes are visible in the properties of VM01 and VM02:

Manage Datacenter Firewall

In System Center 2016 - Virtual Machine Manager (VMM), you can centrally configure and manage Hyper-V port access control lists (ACLs) in your Software Defined Networking (SDN) fabric. These ACLs can be configured for both a Network Controller managed fabric and a non-Network Controller managed fabric. A port access control list (port ACL) is an object that is attached to various networking primitives to describe network security. The port ACL serves as a collection of access control entries or rules. An ACL can be attached to zero or more networking primitives, such as a VM network, VM subnet, virtual network adapter, or the VMM management server itself. An ACL can contain zero or more ACL rules. Each compatible VMM networking primitive (VM network, VM subnet, virtual network adapter, or VMM management server) can have either one port ACL attached or none.

With Virtual Machine Manager 2016 and Windows Server 2016, you now have two flavors of Port ACL available. One that can be applied on Network Controller managed objects (only on NIC and VM Subnet, this type of ACL can't be applied on VMM Servers and VM Networks) and the other that can be applied on non-NC managed objects. If you want a particular ACL to be applicable to NC managed objects you need to use ManagedByNC flag and set it to true. In all other cases the created ACL will only be applicable to non-NC managed objects. The two kinds on ACLs are not interchangeable. So, you can't apply an ACL with ManagedByNC set as false to NC managed objects and vice versa.

One key difference between two kinds of ACL is that while you need remediate each NIC after applying ACL on non-NC objects, no such step is required in case you are applying Port ACLs on NC managed objects. A port ACL entry or rule is an object that describes filtering policy. Multiple ACL rules can exist in the same port ACL and apply based on their priority. Each ACL rule corresponds to exactly one port ACL. Global settings describe a port ACL that is applied to all VM virtual network adapters in the infrastructure. There is no separate object type for Global Settings. Instead, the Global Settings port ACL attaches to the VMM management server itself. The VMM management server object can have either one port ACL or none.

Also, please not the difference in the priority range between NC managed and Non-NC managed ACL rules. For Non-NC managed we support priority from 1- 65535. And for NC managed we support priority from 1 – 64500.

Port ACL settings are exposed only through PowerShell cmdlets in VMM and are not available in the VMM console user interface.

In this lab, we will operate with Network Controller managed firewall rules.

Create Network Controller Managed ACL and Rule

On VMM01 execute the code below to create Network Controller managed ACL:

# Create Port ACL

$PortAcl = New-SCPortACL -Name "TenantAACL" `

-Description "" `

-ManagedByNC;

$PortAcl;

Now let's create ACL rule to deny Inbound connections on Port 80 with priority 200:

# Create Port ACL Rule

New-SCPortACLrule -PortACL $PortAcl `

-Name "Deny80" `

-Description "" `

-Type Inbound `

-Action Deny `

-Priority 200 `

-Protocol Tcp `

-LocalPortRange 80;

Assign Network Controller Managed ACL to Network Adapter

Let's assign this ACL to the network adapters of VM01:

# Assign Port ACL to vNic on VM01

Get-SCVirtualMachine -Name VM01 | `

Get-SCVirtualNetworkAdapter | `

Set-SCVirtualNetworkAdapter -PortACL $PortAcl;

Note that there is no need for VM refresh when Network Controller Managed rules are created.

Test Network Controller Managed ACL

Connect to VM02 via Remote Console and in Internet Explorer enter the IP Address of VM01.

VM02 no longer has http access on port 80 to VM01.

We can test it with PowerShell as well:

Test-NetConnection -ComputerName 192.168.1.8 `

-Port 80;

On VMM01 create another ACL rule in the same ACL with higher priority that allows Port 80:

# Create Port ACL Rule

New-SCPortACLrule -PortACL $PortAcl `

-Name "Allow80" `

-Description "" `

-Type Inbound `

-Action Allow `

-Priority 150 `

-Protocol Tcp `

-LocalPortRange 80;

The access from VM02 to VM01 on port 80 should be restored:

Test-NetConnection -ComputerName 192.168.1.8 `

-Port 80;

Change Network Controller Managed ACL Rule

We can change individual ACL rule properties very easy with two commands. In the code below we are changing the priority of Deny80 rule:

$ACLPortRule = Get-SCPortACLRule -Name "Deny80";

Set-SCPortACLrule -PortACLrule $ACLPortRule `

-Name "Deny80" `

-Priority 149;

We can see that the change is imidiatly applied and access to VM01 to port 80 from VM02 is denied again because Deny80 is with higher priority than Allow80.

Remove Network Controller Managed ACL and Rule

To detach the ACL from the network adapters of VM01 execute the following command on VMM01:

# Remove Port AC Lfrom vNic on VM01

Get-SCVirtualMachine -Name VM01 | `

Get-SCVirtualNetworkAdapter | `

Set-SCVirtualNetworkAdapter –RemovePortACL;

Remove the rules and the ACL using the commands below:Get-SCPortACLrule -Name "Allow80" | Remove-SCPortACLrule;

Get-SCPortACLrule -Name "Deny80" | Remove-SCPortACLrule;

Get-SCPortACL -Name "TenantAACL" | Remove-SCPortACL;

Load Balance VMs with Cloud-Optimized Load Balancer

The SLB enables even distribution of tenant and tenant customer network traffic among virtual network resources, so that multiple servers can host the same workload to provide high availability and scalability.

In this section, we will create Load Balancing Template and Rule to load balance the IIS service on VM01 and VM02.

Create VIP Template

Before creating Sofwtare Load Balancer VIP we need to create a VIP Template. The VIP template defines the rules for load balancing the VMs. Execute the code below to create VIP template on VMM01:

$protocol = New-SCLoadBalancerProtocol -Name "TCP";

$balancingMethod = New-SCLoadBalancingMethod -Name "RoundRobin";

$monitor = @();

New-SCLoadBalancerVIPTemplate -LoadBalancerManufacturer "Microsoft" `

-LoadBalancerModel "Microsoft Network Controller" `

-LoadBalancerHealthMonitor $monitor `

-Name "HTTP-80-80" `

-Description "" `

-LoadBalancingMethod $balancingMethod `

-LoadBalancerProtocol $protocol `

-LoadBalancerPort 80 `

-LoadBalancerBackEndPort "80";

After successful creation, the VIP template appears in VMM console -> Fabric pane -> Networking ->VIP Templates:

Create a SLB VIP

Execute the script below on VMM01 to create SLB VIP for VM01 and VM02:

function CreateTenantVIP {

param(

[Parameter(Mandatory=$false)]

# Name of the Network Controller Network Service

# This value should be the name you gave the Network Controller service

# when you on-boarded the Network Controller to VMM

$LBServiceName = "Network Controller",

[Parameter(Mandatory=$false)]

# Name of the VM instances to which you want to assign the VIP

$VipMemberVMNames = @("VM01", "VM02"),

[Parameter(Mandatory=$false)]

# VIP address you want to assign from the VIP pool.

# Pick any VIP that falls within your VIP IP Pool range.

$VipAddress = "41.40.40.12",

[Parameter(Mandatory=$false)]

# Name of the VIP VM Network

$VipNetworkName = "PublicVIP",

[Parameter(Mandatory=$false)]

# The name of the VIP template you created via the VMM Console.

$VipTemplateName = "HTTP-80-80",

[Parameter(Mandatory=$false)]

# Arbitrary but good to match the VIP you're using.

$VipName = "TenantAVIPWebTest"

)

Import-Module virtualmachinemanager

$lb = Get-SCLoadBalancer | where { $_.Service.Name -eq $LBServiceName};

$vipNetwork = Get-SCVMNetwork -Name $VipNetworkName;

$vipMemberNics = @();

foreach ($vmName in $VipMemberVMNames)

{

$vm = Get-SCVirtualMachine -Name $vmName;

$vipMemberNics += $vm.VirtualNetworkAdapters[0];

}

$existingVip = Get-SCLoadBalancerVIP -Name $VipName

if ($existingVip -ne $null)

{

foreach ($mem in $existingVip.VipMembers)

{

$mem | Remove-SCLoadBalancerVIPMember | Out-Null;

}

$existingVip | Remove-SCLoadBalancerVIP | Out-Null;

}

$vipt = Get-SCLoadBalancerVIPTemplate -Name $VipTemplateName;

$vip = New-SCLoadBalancerVIP -Name $VipName `

-LoadBalancer $lb `

-IPAddress $VipAddress `

-LoadBalancerVIPTemplate $vipt `

-FrontEndVMNetwork $vipNetwork `

-BackEndVirtualNetworkAdapters $vipMemberNics;

Write-Output "Created VIP " $vip;

$vip = get-scloadbalancervip -Name $VipName;

Write-Output "VIP with members " $vip.VIPMembers;

}

CreateTenantVIP -LBServiceName "Network Controller" `

-VipMemberVMNames @("VM01", "VM02") `

-VipAddress "41.40.40.12" `

-VipNetworkName "PublicVIP" `

-VipTemplateName "HTTP-80-80" `

-VipName "TenantAVIPWebTest";

The routing information for 41.40.40.12 can be verified by executing the command below on DC01:Get-BgpRouteInformation;

SLB functionality can be validated by opening Internet Explorer on VMM01 and enter 41.40.40.12 in the address bar. In the case below I've put two different Titles in the default site page on VM01 and VM02 to distinguish them and opened IE side by side.

Create Site to Site VPN with Hybrid SDN Gateway

In this section, we will setup S2S VPN between Tenant network and Remote Site.

Create Run As Account for VPN Passphrase

S2S VPN connection will be established via Passphrase. We need to create a run As account that will use for the VPN configuration on the Tenant in VMM. Create run as account with password User@123 by execuiting the code below in VMM01:

$secpasswd = ConvertTo-SecureString "User@123" `

-AsPlainText `

-Force;

$PassphraseCreds = New-Object System.Management.Automation.PSCredential ("IPSEC", $secpasswd);

$runAsAccount = New-SCRunAsAccount -Credential $PassphraseCreds `

-Name "IPSECACCOUNT" `

-Description "" `

-NoValidation;

Create S2S VPN connection on VM Network

Execute the code below on VMM01 to create S2S VPN connection for TenantA network to a remote endpoint 50.50.50.1:

# Variables

$RemoteEndpont = "50.50.50.1"

$RemoteIPSubnet = "60.60.60.0/24"

# Get Tenant Network

$vmNetwork = Get-SCVMNetwork -Name TenantA;

# Get Tenant Subnet

$vmSubnet = Get-SCVMSubnet -Name "Subnet1" `

-VMNetwork $vmNetwork;

# Get NC

$gatewayDevice = Get-SCNetworkGateway -Name "Network Controller";

# Add Gateway to Tenant Network

$VmNetworkGateway = Add-SCVMNetworkGateway -Name "TenantA_Gateway" `

-EnableBGP $false `

-NetworkGateway $gatewayDevice `

-VMNetwork $vmNetwork `

-RoutingIPSubnet "10.254.254.0/29";

# Get Run As Account for Passphrase

$runAsAccount = Get-SCRunAsAccount -Name IPSECACCOUNT;

# Create VPN connection

$vpnConnection = Add-SCVPNConnection -AuthenticationMethod "PSKOnly" `

-AuthenticationTransformConstants "SHA196" `

-CipherTransformConstants "AES256" `

-DHGroup "Group2" `

-EncryptionMethod "AES256" `

-IntegrityCheckMethod "SHA1" `

-PFSGroup "PFS2048" `

-Protocol "IKEv2" `

-Name "IPSEC-VMM" `

-TargetIPv4VPNAddress $RemoteEndpont `

-Secret $runAsAccount `

-VMNetworkGateway $VmNetworkGateway;

# Add route to VPN Connection

Add-SCNetworkRoute -IPSubnet $RemoteIPSubnet `

-VPNConnection $vpnConnection `

-VMNetworkGateway $VmNetworkGateway;

After the connection is created it will be visible in VMM console -> VMs and Services pane - > VM Networks -> Tenant A -> Properties -> VPN Connections:

Create S2S VPN Connection on Remote Server

On Remote Server, we will create S2S VPN connection to connect to the VPN connection for TenantA network. Open Routing and Remote Access console on Remote, right click "Network Interface" and select "New Demand-dial Interface".

Type IPSecVPN for Interface name and click Next.

On Connection Type page select Connect using virtual private network (VPN) and click Next.

On VPN Type page select IKEv2 and click Next.

For Destination Address take a note on the Public IPv4 address from Service Information for Gaetway Manager role of Network Controller properties. Enter the IP and click next.

On Protocols and Security page check Route IP packets on this interface and click Next.

On Static Routes for Remote Networks add record for 192.168.1.0/24 network and click Next.

On Dial Out credentials page click Next and then Finish.

Right click on the created IPSecVPN interface and select Properties. Click on Options tab. Change connection type for Persistent.

Click on Security tab. Select User preshared key for authentication. In the Key field enter User@123 for password and click OK to save the settings.

Open PowerShell as Administrator on Remote and execute the following command to modify the properties of the S2S VPN connection:

# Set S2S VPN interface

Set-VpnS2SInterface -Name IPSecVPN `

-CustomPolicy `

-AuthenticationTransformConstants SHA196 `

-CipherTransformConstants AES256 `

-DHGroup Group2 `

-EncryptionMethod AES256 `

-IntegrityCheckMethod SHA1 `

-PfsGroup PFS2048;

Add persistent route in order to reach 41.40.40.0/24 network:

# Add persistent route

route -p add 41.40.40.0 mask 255.255.255.0 50.50.50.2

Right click on IPSecVPN and select Connect. After a short delay, around 5-6 seconds, you will see the IPSecVPN Connection State change from Disconnected to Connected.

Now from Remote VM you can reach both VM01 and VM02 on the local network (192.168.1.0/24) and external network (41.40.40.0/24).

From VM01 or VM02 access 60.60.60.1 address via Internet Explorer:

As you can see it is two way communication and routing works between 60.60.60.0/24 network and 192.168.1.0/24 network.

References

Storage in Windows Server 2016

https://technet.microsoft.com/en-us/windows-server-docs/storage/storage-spaces/hyper-converged-solution-using-storage-spaces-direct

Networking in Windows Server 2016

https://technet.microsoft.com/en-us/windows-server-docs/networking/networking

Compute in Windows Server 2016

https://technet.microsoft.com/en-us/windows-server-docs/compute/compute

Hyper-converged solution using Storage Spaces Direct in Windows Server 2016

https://technet.microsoft.com/en-us/windows-server-docs/storage/storage-spaces/hyper-converged-solution-using-storage-spaces-direct

Run Hyper-V in a Virtual Machine with Nested Virtualization

https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/user_guide/nesting?f=255&MSPPError=-2147217396

Virtual Machine Manager (VMM)

https://technet.microsoft.com/en-us/system-center-docs/vmm/vmm

SDNExpress and VMMExpress

https://github.com/Microsoft/SDN

Deploy Network Controller using Windows PowerShell

https://technet.microsoft.com/en-us/windows-server-docs/networking/sdn/deploy/deploy-network-controller-using-windows-powershell

Set up a Software Defined Network (SDN) infrastructure in the VMM fabric

https://technet.microsoft.com/en-us/system-center-docs/vmm/scenario/sdn-overview

Set up an SDN network controller in the VMM fabric

https://technet.microsoft.com/en-us/system-center-docs/vmm/scenario/sdn-network-controller

Set up an SDN software load balancer in the VMM fabric

https://technet.microsoft.com/en-us/system-center-docs/vmm/scenario/sdn-slb

Set up an SDN RAS gateway in the VMM fabric

https://technet.microsoft.com/en-us/system-center-docs/vmm/scenario/sdn-gateway

Set up Software Defined Network (SDN) components in the VMM fabric using PowerShell

https://technet.microsoft.com/en-us/system-center-docs/vmm/scenario/sdn-powershell

Deploying SDN on One single physical host using VMM – Initial Setup and Network Controller

https://blogs.msdn.microsoft.com/excellentsge/2016/10/06/deploying-sdn-on-one-single-physical-host-using-vmm/

Deploying SDN on One single physical host using VMM – Software Load Balancer

https://blogs.msdn.microsoft.com/excellentsge/2016/10/07/deploying-sdn-on-one-single-physical-host-using-vmm-software-load-balancer/

Deploying SDN on One single physical host using VMM – RAS Gateway

https://blogs.msdn.microsoft.com/excellentsge/2016/10/07/deploying-sdn-on-one-single-physical-host-using-vmm-ras-gateway/

Manage Hyper-V extended port ACLs in VMM

https://technet.microsoft.com/en-us/system-center-docs/vmm/manage/manage-compute-extended-port-acls

Windows ADK

https://developer.microsoft.com/en-us/windows/hardware/windows-assessment-deployment-kit#winADK


Software Defined Data Center Scripts

2.2 - Setup Environment.ps1

#region Create Hyper-V switches
New-VMSwitch -SwitchName "SDN" `
             -SwitchType Internal;

New-VMSwitch -SwitchName "Public" `
             -SwitchType Internal;

New-VMSwitch -SwitchName "Private" `
             -SwitchType Internal;
#endregion

#region Sysprep VHDs
# Check for Available Updates
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate `
                      -ClassName MSFT_WUOperationsSession;  
# Scan for Updates
$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates `
                                 -Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true};

# Show Updates found for install
$result.Updates;

# Initiate Update and restart
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate `
                      -ClassName MSFT_WUOperationsSession;
# Apply Updates
Invoke-CimMethod -InputObject $ci `
                 -MethodName ApplyApplicableUpdates;

# Restart Server
Restart-Computer; exit;

# Enable RDP
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" `
                 -Name "fDenyTSConnections" `
                 -Value 0;
Enable-NetFirewallRule -DisplayGroup "Remote Desktop";


# Sysprep
C:\Windows\system32\sysprep\sysprep.exe /oobe /generalize /shutdown

#endregion

#region Create and configure Domain Controller VM
# VM name
$VMName = "DC01";

# Storage Path
$StoragePath  = Read-Host `
             -Prompt 'Enter path to storage where VMs will be stored (example C:\ClusterStorage\Volume1) ';

# OS VHDs location
$osVHDPath = Read-Host `
             -Prompt 'Enter syspreped VHDs path (Windows Server 2016 DataCenter (with Desktop Experience) (example C:\ClusterStorage\Volume1\WS2016DC-DE.vhdx) ';
$OSVHDname = $osVHDPath.Split("\")[-1]


# Create VM
New-VM -Name $VMName `
       -Path "$StoragePath\" `
       -Generation 2 `
       -SwitchName "SDN" `
       -MemoryStartupBytes 4GB ;

# Set Proc number
Set-VM -Name $VMName `
       -ProcessorCount 2;

# Copy OS VHD
Copy-Item -Path $osVHDPath `
          -Destination "$StoragePath\$VMName\" `
          -Force;

# Add OS vhd to VM
Add-VMHardDiskDrive -VMName $VMName `
                    -Path "$StoragePath\$VMName\$OSVHDname";

# Get OS VHD
$vhd = Get-VMHardDiskDrive -VMName $VMName;

# Set boot order
Set-VMFirmware -VMName $VMName `
               -FirstBootDevice $vhd;

Set-VMNetworkAdapterVlan -VMName $VMName `
                         -VlanId 7 `
                         -Access; 

# Rname Network adapter
Get-VMNetworkAdapter -VMName $VMName | Rename-VMNetworkAdapter -NewName "MGMT";
Set-VMNetworkAdapter -Name "MGMT" `
                     -VMName $VMName `
                     -DeviceNaming On;


# Start VM
Start-VM -Name $VMName;





# Configure MGMT interface
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 10.0.0.1 `
                 –PrefixLength 24;

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 127.0.0.1; 

# Rename the server
Rename-Computer -NewName "DC01" `
                -Restart;

# List Time Zones
tzutil /l | more 
# Set Time Zone
tzutil /s "" 




# VM name
$VMName = "DC01";
#Add Additional adapters
Add-VMNetworkAdapter -VMName $VMName `
                     -SwitchName "SDN" `
                     -Name "Transit" `
                     -DeviceNaming On;

Set-VMNetworkAdapterVlan -VMNetworkAdapterName "Transit" `
                         -VMName $VMName `
                         -VlanId 10 `
                         -Access;

Add-VMNetworkAdapter -VMName $VMName `
                     -SwitchName "SDN" `
                     -Name "HNV" `
                     -DeviceNaming On;


Set-VMNetworkAdapterVlan -VMNetworkAdapterName "HNV" `
                         -VMName $VMName `
                         -VlanId 11 `
                         -Access;

Add-VMNetworkAdapter -VMName $VMName `
                     -SwitchName "Public" `
                     -Name "Public" `
                     -DeviceNaming On;



# Configure Transit interface
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "Transit"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 10.10.10.1 `
                 –PrefixLength 24;

Get-DnsClient -InterfaceAlias $Interface | Set-DnsClient -RegisterThisConnectionsAddress $false

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 10.0.0.1; 


# Configure HNV interface
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "HNV"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 10.10.56.1 `
                 –PrefixLength 24;

Get-DnsClient -InterfaceAlias $Interface | Set-DnsClient -RegisterThisConnectionsAddress $false

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 10.0.0.1; 


# Configure Public interface
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "Public"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 50.50.50.2 `
                 –PrefixLength 24;

Get-DnsClient -InterfaceAlias $Interface | Set-DnsClient -RegisterThisConnectionsAddress $false

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 10.0.0.1; 

#endregion

#region Create and Configure Storage Spaces Direct Nodes
# Executed on the Hyper-V host
# VM names
$VMNames = ('S2D01', 'S2D02', 'S2D03', 'S2D04');

# Storage Path
$StoragePath  = Read-Host `
             -Prompt 'Enter path to storage where VMs will be stored (example C:\ClusterStorage\Volume1) ';

# OS VHDs location
$osVHDPath = Read-Host `
             -Prompt 'Enter syspreped VHDs path (Windows Server 2016 DataCenter (Core) (example C:\ClusterStorage\Volume1\WS2016DC-Core.vhdx) ';
$OSVHDname = $osVHDPath.Split("\")[-1]


Foreach($VMName in $VMNames )
{
    
    # Create VM
    New-VM -Name $VMName `
           -Path "$StoragePath\$VMName" `
           -Generation 2 `
           -SwitchName "SDN" `
           -MemoryStartupBytes 16GB ;
    
    # Set Proc number
    Set-VM -Name $VMName `
           -ProcessorCount 8;

    # Copy OS VHD
    Copy-Item -Path $osVHDPath `
              -Destination "$StoragePath\$VMName\" `
              -Force;

    # Add OS vhd to VM
    Add-VMHardDiskDrive -VMName $VMName `
                        -Path "$StoragePath\$VMName\$OSVHDname";

    # Get OS VHD
    $vhd = Get-VMHardDiskDrive -VMName $VMName;

    # Set boot order
    Set-VMFirmware -VMName $VMName `
                   -FirstBootDevice $vhd;

    Set-VMNetworkAdapterVlan -VMName $VMName `
                         -Trunk `
                         -AllowedVlanIdList 0-11 `
                         -NativeVlanId 7; 

    # Rname Network adapter
    Get-VMNetworkAdapter -VMName $VMName | Rename-VMNetworkAdapter -NewName "MGMT";
    Set-VMNetworkAdapter -Name "MGMT" `
                         -VMName $VMName `
                         -DeviceNaming On;

    # Start VM
    Start-VM -Name $VMName;
}; 


# Configure MGMT interface on S2D01
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 10.0.0.211 `
                 –PrefixLength 24 `
                 -DefaultGateway 10.0.0.1;

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 10.0.0.1; 

# Configure MGMT interface on S2D02
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 10.0.0.212 `
                 –PrefixLength 24 `
                 -DefaultGateway 10.0.0.1;

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 10.0.0.1; 


# Configure MGMT interface on S2D03
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 10.0.0.213 `
                 –PrefixLength 24 `
                 -DefaultGateway 10.0.0.1;

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 10.0.0.1;



# Configure MGMT interface on S2D04
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 10.0.0.214 `
                 –PrefixLength 24 `
                 -DefaultGateway 10.0.0.1;

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 10.0.0.1;


# List Time Zones
tzutil /l | more 
# Set Time Zone
tzutil /s "" 


# Join S2D01 to domain
# Join computer to domain and restart
Add-Computer -DomainName "dms.int" `
             -Force `
             -Restart `
             -NewName "S2D01"; 

# Join S2D02 to domain
# Join computer to domain and restart
Add-Computer -DomainName "dms.int" `
             -Force `
             -Restart `
             -NewName "S2D02"; 

# Join S2D03 to domain
# Join computer to domain and restart
Add-Computer -DomainName "dms.int" `
             -Force `
             -Restart `
             -NewName "S2D03"; 


# Join S2D04 to domain
# Join computer to domain and restart
Add-Computer -DomainName "dms.int" `
             -Force `
             -Restart `
             -NewName "S2D04"; 




# Add domain group to local administrators group
Net localgroup Administrators "dms\S2DAdmins" /add 


# Execute on Hyper-V host
# VM names
$VMNames = ('S2D01', 'S2D02', 'S2D03', 'S2D04');
Foreach($VMName in $VMNames )
{
    # Shut Down the VM
    Stop-VM –Name $VMName;
    
    # Enable Nested Virtualization
    Set-VMProcessor -VMName $VMName `
                    -ExposeVirtualizationExtensions $true;
    
    # Enable MAC Address Spoofing
    Get-VMNetworkAdapter -VMName $VMName | Set-VMNetworkAdapter -MacAddressSpoofing On;
    
    # Start VM
    Start-VM -Name $VMName;
}; 


# Add Additional disks
# Execute on Hyper-V host
$VMNames = ('S2D01', 'S2D02', 'S2D03', 'S2D04');
Foreach ($VMName in $VMNames){
   
   # SSD Disk Names
   $SSDDiskNames =  ("SSD01.vhdx", "SSD02.vhdx");

   # Create and attach SDD disks
   foreach ($SSDDiskName in $SSDDiskNames )
   {
        $diskName = $SSDDiskName;
        
        # Get the VM
        $VM = Get-VM -Name $VMName;
        
        # Get VM Location
        $VMLocation = $VM.Path;
        
        # Set Disk Size in GB
        $Disksize = 256;
        $DisksizeinBytes = $Disksize*1024*1024*1024;

        # Create Disk
        $VHD = New-VHD -Path  "$VMLocation\$diskName" `
                       -Dynamic `
                       -SizeBytes $DisksizeinBytes;
        
        # Atach the disk
        $AddedSharedVHDX = ADD-VMHardDiskDrive -VMName $VM.Name `
                                               -Path             "$VMLocation\$diskName" `
                                               -ControllerType   SCSI `
                                               -ControllerNumber 0;
       
   };
   # HHD Disk Names
   $HDDDiskNames =  ("HDD01.vhdx", "HDD02.vhdx", "HDD03.vhdx");
   
   # Create and attach HDD disks
   foreach ($HDDDiskName in $HDDDiskNames )
   {
        $diskName = $HDDDiskName;

        # Get the VM
        $VM = Get-VM -Name $VMName;
        
        # Get VM Location
        $VMLocation = $VM.Path;
        
        # Set Disk Size in GB
        $Disksize = 512;
        $DisksizeinBytes = $Disksize*1024*1024*1024;
        
        # Create Disk
        $VHD = New-VHD -Path  "$VMLocation\$diskName" `
                       -Dynamic `
                       -SizeBytes $DisksizeinBytes;
        
        # Atach the disk
        $AddedSharedVHDX = ADD-VMHardDiskDrive -VMName $VM.Name `
                                               -Path             "$VMLocation\$diskName" `
                                               -ControllerType   SCSI `
                                               -ControllerNumber 0;
       
   };
   
}; 


# Install Roles
Install-WindowsFeature -Name File-Services
Install-WindowsFeature -Name Failover-clustering -IncludeManagementTools
Install-WindowsFeature -Name Hyper-V -IncludeManagementTools -Restart 


# Validate Cluster
$nodes = ("S2D01", "S2D02", "S2D03", "S2D04"); 

# Validate Cluster Configuration
Test-Cluster –Node $nodes `
             –Include "Storage Spaces Direct","Inventory","Network","System Configuration"; 

# Create Cluster
$nodes = ("S2D01", "S2D02", "S2D03", "S2D04"); 

# Create Cluster with no storage
New-Cluster –Name S2D-CLU `
            –Node $nodes  `
            –NoStorage `
            –StaticAddress 10.0.0.215;


# Enable Storage Spaces Direct
Enable-ClusterStorageSpacesDirect -CimSession S2D-CLU `
                                  -Confirm:$false; 

# Remove all Storage Tiers
Get-StorageTier -CimSession S2D-CLU | Remove-StorageTier -Confirm:$false `
                                                         -CimSession S2D-CLU; 


# Change Media Type
Get-StoragePool -FriendlyName "S2D*" `
                -CimSession S2D-CLU | `
                Get-PhysicalDisk -CimSession S2D-CLU | `
                ? Size           -lt 300GB ` | `
                Set-PhysicalDisk -CimSession S2D-CLU `
                                 –MediaType SSD;

Get-StoragePool -FriendlyName "S2D*" `
                -CimSession S2D-CLU  | `
                Get-PhysicalDisk -CimSession S2D-CLU | `
                ? Size           -gt 300GB | `
                Set-PhysicalDisk -CimSession S2D-CLU `
                                 –MediaType HDD; 


# Create Tiers
Get-StoragePool -FriendlyName "S2D*" `
                -CimSession S2D-CLU  | `
                New-StorageTier –FriendlyName Performance `
                                –MediaType SSD `
                                -ResiliencySettingName Mirror `
                                -CimSession S2D-CLU;

Get-StoragePool -FriendlyName "S2D*" `
                -CimSession S2D-CLU | `
                New-StorageTier –FriendlyName Capacity `
                                –MediaType HDD `
                                -ResiliencySettingName Parity `
                                -CimSession S2D-CLU;


#endregion

#region Create and Configure VMM Server

# VM name
$VMName = "VMM01";

# Storage Path
$StoragePath  = Read-Host `
             -Prompt 'Enter path to storage where VMs will be stored (example C:\ClusterStorage\Volume1) ';

# OS VHDs location
$osVHDPath = Read-Host `
             -Prompt 'Enter syspreped VHDs path (Windows Server 2016 DataCenter (with Desktop Experience) (example C:\ClusterStorage\Volume1\WS2016DC-DE.vhdx) ';
$OSVHDname = $osVHDPath.Split("\")[-1]


# Create VM
New-VM -Name $VMName `
       -Path "$StoragePath\" `
       -Generation 2 `
       -SwitchName "SDN" `
       -MemoryStartupBytes 4GB ;

# Set Proc number
Set-VM -Name $VMName `
       -ProcessorCount 4;

# Copy OS VHD
Copy-Item -Path $osVHDPath `
          -Destination "$StoragePath\$VMName\" `
          -Force;

# Add OS vhd to VM
Add-VMHardDiskDrive -VMName $VMName `
                    -Path "$StoragePath\$VMName\$OSVHDname";

# Get OS VHD
$vhd = Get-VMHardDiskDrive -VMName $VMName;

# Set boot order
Set-VMFirmware -VMName $VMName `
               -FirstBootDevice $vhd;

# Set Disk Size in GB
$Disksize = 200;
$DisksizeinBytes = $Disksize*1024*1024*1024;
    
# Create Disk
$VHD = New-VHD -Path  "$StoragePath\$VMName\HDD01.vhdx" `
               -Dynamic `
               -SizeBytes $DisksizeinBytes;
    
# Atach the disk
$AddedVHDX = ADD-VMHardDiskDrive -VMName $VMName `
                                 -Path             "$StoragePath\$VMName\HDD01.vhdx" `
                                 -ControllerType   SCSI `
                                 -ControllerNumber 0;


Set-VMNetworkAdapterVlan -VMName $VMName `
                         -VlanId 7 `
                         -Access; 

# Rname Network adapter
Get-VMNetworkAdapter -VMName $VMName | Rename-VMNetworkAdapter -NewName "MGMT";
Set-VMNetworkAdapter -Name "MGMT" `
                     -VMName $VMName `
                     -DeviceNaming On;


# Start VM
Start-VM -Name $VMName;




# Configure MGMT interface
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "MGMT"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 10.0.0.225 `
                 –PrefixLength 24 `
                 -DefaultGateway 10.0.0.1;

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 10.0.0.1; 


# List Time Zones
tzutil /l | more 
# Set Time Zone
tzutil /s "" 


# Join VMM01 to domain
# Join computer to domain and restart
Add-Computer -DomainName "dms.int" `
             -Force `
             -Restart `
             -NewName "VMM01"; 


Invoke-Command -ComputerName VMM01 -ScriptBlock {
    $RawDisks = Get-Disk | where partitionstyle -eq 'raw'| Sort-Object Number;
    foreach ($disk in $RawDisks)
    {
        $InitializedDisk = Initialize-Disk -PartitionStyle GPT `
                                           -Number         $disk.Number `
                                           -ErrorAction Stop;
    
        $FormatedVol = New-Partition -DiskNumber     $Disk.Number `
                                     -UseMaximumSize `
                                     -DriveLetter T `
                                     -ErrorAction Stop | `
                                     Format-Volume -FileSystem         NTFS `
                                                   -AllocationUnitSize 65536 `
                                                   -NewFileSystemLabel Data `
                                                   -Force `
                                                   -Confirm:$false `
                                                   -ErrorAction Stop;
    };
}; 



Invoke-Command –Computername VMM01 `
               –ScriptBlock {
               Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" `
                                -Name "fDenyTSConnections" `
                                –Value 0;
                Enable-NetFirewallRule -DisplayGroup "Remote Desktop";
               }; 


#endregion

#region Install SQL Server 2016 on VMM

#endregion

#region Install Virtual Machine Manager 2016

# Add VMM service account to local administrators group
Net localgroup Administrators "dms\_VMMService" /add  


# Update VMM Server
msiexec.exe /update 

# Update VMM consple
msiexec.exe /update  
#endregion

#region Create and configure Remote Server
# VM name
$VMName = "Remote";

# Storage Path
$StoragePath  = Read-Host `
             -Prompt 'Enter path to storage where VMs will be stored (example C:\ClusterStorage\Volume1) ';

# OS VHDs location
$osVHDPath = Read-Host `
             -Prompt 'Enter syspreped VHDs path (Windows Server 2016 DataCenter (with Desktop Experience) (example C:\ClusterStorage\Volume1\WS2016DC-DE.vhdx) ';
$OSVHDname = $osVHDPath.Split("\")[-1]


# Create VM
New-VM -Name $VMName `
       -Path "$StoragePath\" `
       -Generation 2 `
       -SwitchName "Private" `
       -MemoryStartupBytes 2GB ;

# Set Proc number
Set-VM -Name $VMName `
       -ProcessorCount 2;

# Copy OS VHD
Copy-Item -Path $osVHDPath `
          -Destination "$StoragePath\$VMName\" `
          -Force;

# Add OS vhd to VM
Add-VMHardDiskDrive -VMName $VMName `
                    -Path "$StoragePath\$VMName\$OSVHDname";

# Get OS VHD
$vhd = Get-VMHardDiskDrive -VMName $VMName;

# Set boot order
Set-VMFirmware -VMName $VMName `
               -FirstBootDevice $vhd;


# Rename Network adapter
Get-VMNetworkAdapter -VMName $VMName | Rename-VMNetworkAdapter -NewName "Private";
Set-VMNetworkAdapter -Name "Private" `
                     -VMName $VMName `
                     -DeviceNaming On;
# Add Public Adapter
Add-VMNetworkAdapter -VMName $VMName `
                     -SwitchName "Public" `
                     -Name "Public" `
                     -DeviceNaming On;
# Start VM
Start-VM -Name $VMName;



# Configure Public interface
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "Public"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 50.50.50.1 `
                 –PrefixLength 24;
# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 50.50.50.2; 

# Configure Private interface
$Interface = Get-NetAdapterAdvancedProperty –DisplayName "Hyper-V Network Adapter Name" | where {$_.DisplayValue -eq "Private"} | select -ExpandProperty InterfaceAlias

# Remove Interface configuration
Remove-NetIPAddress -InterfaceAlias $Interface `
                    -Confirm:$false ;

# Configure IP settings on interface
New-NetIPAddress –InterfaceAlias $Interface `
                 –IPAddress 60.60.60.1 `
                 –PrefixLength 24;

# Set DNS
Set-DnsClientServerAddress -InterfaceAlias $Interface `
                           -ServerAddresses 60.60.60.1; 


# Rename the server
Rename-Computer -NewName "Remote" `
                -Restart; 

# List Time Zones
tzutil /l | more 

# Set Time Zone
tzutil /s "" 

# Reset IIS
iisreset

#endregion

2.3.1 - Setup Software Defined Storage and Compute.ps1

#region Create and Configure Hyper-V hosts Management Account
$GUID = [guid]::NewGuid();
$credential = Get-Credential;
$runAsAccount = New-SCRunAsAccount -Credential $credential `
                                   -Name "Hyper-V Hosts Admin" `
                                   -Description "" `
                                   -JobGroup $GUID.Guid;
#endregion

#region Create Management Network
# Create Logical Network
$logicalNetwork = New-SCLogicalNetwork -Name "Management" `
                                       -LogicalNetworkDefinitionIsolation $false `
                                       -EnableNetworkVirtualization $false `
                                       -UseGRE $false `
                                       -IsPVLAN $false;

$allHostGroups = @()
$allHostGroups +=  Get-SCVMHostGroup -Name "All Hosts";
$allSubnetVlan = @()
$allSubnetVlan += New-SCSubnetVLan -Subnet "10.0.0.0/24" `
                                   -VLanID 0;

New-SCLogicalNetworkDefinition -Name "Management_0" `
                               -LogicalNetwork $logicalNetwork `
                               -VMHostGroup $allHostGroups `
                               -SubnetVLan $allSubnetVlan `
                               -RunAsynchronously;


New-SCVMNetwork -Name "Management" `
                -IsolationType "NoIsolation" `
                -LogicalNetwork $logicalNetwork;


# Create IP Pool
# Get Logical Network 'Management'
$logicalNetwork = Get-SCLogicalNetwork -Name Management;
# Get Logical Network Definition 'Management_0'
$logicalNetworkDefinition = Get-SCLogicalNetworkDefinition -Name Management_0;

# Network Routes
$allNetworkRoutes = @();

# Gateways
$allGateways = @();
$allGateways += New-SCDefaultGateway -IPAddress "10.0.0.1" `
                                     -Automatic;

# DNS servers
$allDnsServer = @("10.0.0.1");

# DNS suffixes
$allDnsSuffixes = @();

# WINS servers
$allWinsServers = @();

New-SCStaticIPAddressPool -Name "Management IP Pool" `
                          -LogicalNetworkDefinition $logicalNetworkDefinition `
                          -Subnet "10.0.0.0/24" `
                          -IPAddressRangeStart "10.0.0.211" `
                          -IPAddressRangeEnd "10.0.0.240" `
                          -DefaultGateway $allGateways `
                          -DNSServer $allDnsServer `
                          -DNSSuffix "" `
                          -DNSSearchSuffix $allDnsSuffixes `
                          -NetworkRoute $allNetworkRoutes `
                          -RunAsynchronously `
                          -IPAddressReservedSet "10.0.0.225";

#endregion

#region Create Logical Switch
$logicalSwitch = New-SCLogicalSwitch -Name "Prod" `
                                     -Description "" `
                                     -EnableSriov $false `
                                     -SwitchUplinkMode "EmbeddedTeam" `
                                     -MinimumBandwidthMode "Weight";

# Get Network Port Classification 'Network load balancing'
$portClassification = Get-SCPortClassification -Name "Network load balancing";
# Get Hyper-V Switch Port Profile 'Network load balancer NIC Profile'
$nativeProfile = Get-SCVirtualNetworkAdapterNativePortProfile -Name "Network load balancer NIC Profile";
New-SCVirtualNetworkAdapterPortProfileSet -Name "Network load balancing" `
                                          -PortClassification $portClassification `
                                          -LogicalSwitch $logicalSwitch `
                                          -RunAsynchronously `
                                          -VirtualNetworkAdapterNativePortProfile $nativeProfile;

# Get Network Port Classification 'Medium bandwidth'
$portClassification = Get-SCPortClassification -Name "Medium bandwidth";
# Get Hyper-V Switch Port Profile 'Medium Bandwidth Adapter'
$nativeProfile = Get-SCVirtualNetworkAdapterNativePortProfile -Name "Medium Bandwidth Adapter";
New-SCVirtualNetworkAdapterPortProfileSet -Name "Medium bandwidth" `
                                          -PortClassification $portClassification `
                                          -LogicalSwitch $logicalSwitch `
                                          -RunAsynchronously `
                                          -VirtualNetworkAdapterNativePortProfile $nativeProfile;

# Get Network Port Classification 'Host management'
$portClassification = Get-SCPortClassification -Name "Host management";
# Get Hyper-V Switch Port Profile 'Host management'
$nativeProfile = Get-SCVirtualNetworkAdapterNativePortProfile -Name "Host management";
New-SCVirtualNetworkAdapterPortProfileSet -Name "Host management" `
                                          -PortClassification $portClassification `
                                          -LogicalSwitch $logicalSwitch `
                                          -RunAsynchronously `
                                          -IsDefaultPortProfileSet $true `
                                          -VirtualNetworkAdapterNativePortProfile $nativeProfile;
$definitions = @()
# Get Logical Network Definition 'Management_0'
$definitions += Get-SCLogicalNetworkDefinition -Name Management_0;

$nativeUppVar = New-SCNativeUplinkPortProfile -Name "ProdUplink" `
                                              -Description "" `
                                              -LogicalNetworkDefinition $definitions `
                                              -EnableNetworkVirtualization $false `
                                              -LBFOLoadBalancingAlgorithm "HostDefault" `
                                              -LBFOTeamMode "SwitchIndependent" `
                                              -RunAsynchronously;

$uppSetVar = New-SCUplinkPortProfileSet -Name "ProdUplink" `
                                        -LogicalSwitch $logicalSwitch `
                                        -NativeUplinkPortProfile $nativeUppVar `
                                        -RunAsynchronously;

# Get VM Network 'Management'
$vmNetwork = Get-SCVMNetwork -Name Management;
# Get Network Port Classification 'Host management'
$vNICPortClassification = Get-SCPortClassification -Name "Host Management";
New-SCLogicalSwitchVirtualNetworkAdapter -Name "Management" `
                                         -UplinkPortProfileSet $uppSetVar `
                                         -RunAsynchronously `
                                         -VMNetwork $vmNetwork `
                                         -VLanEnabled $false `
                                         -PortClassification $vNICPortClassification `
                                         -IsUsedForHostManagement $true `
                                         -InheritsAddressFromPhysicalNetworkAdapter $true `
                                         -IPv4AddressType "Dynamic" `
                                         -IPv6AddressType "Dynamic";

#endregion

#region Create Hyper-V Switch on Hyper-V Hosts

# Set S2D01
# Generate Job GUID
$GUID = [guid]::NewGuid();

# Get Host 'S2D01.dms.int'
$vmHost = Get-SCVMHost -ComputerName S2D01;

# Get Host Network Adapter 'Microsoft Hyper-V Network Adapter'
$networkAdapter = Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `
                                             -VMHost S2D01;

$uplinkPortProfileSet = Get-SCUplinkPortProfileSet -Name ProdUplink;

Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $networkAdapter `
                           -UplinkPortProfileSet $uplinkPortProfileSet `
                           -JobGroup $GUID.Guid;
$networkAdapter = @();
$networkAdapter += Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `
                                              -VMHost S2D01;

$logicalSwitch = Get-SCLogicalSwitch -Name Prod;

New-SCVirtualNetwork -VMHost $vmHost `
                     -VMHostNetworkAdapters $networkAdapter `
                     -LogicalSwitch $logicalSwitch `
                     -DeployVirtualNetworkAdapters `
                     -JobGroup $GUID.Guid;


Set-SCVMHost -VMHost $vmHost `
             -JobGroup $GUID.Guid `
             -RunAsynchronously;


# Set S2D02
# Generate Job GUID
$GUID = [guid]::NewGuid();

# Get Host 'S2D02.dms.int'
$vmHost = Get-SCVMHost -ComputerName S2D02;

# Get Host Network Adapter 'Microsoft Hyper-V Network Adapter'
$networkAdapter = Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `
                                             -VMHost S2D02;

$uplinkPortProfileSet = Get-SCUplinkPortProfileSet -Name ProdUplink;

Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $networkAdapter `
                           -UplinkPortProfileSet $uplinkPortProfileSet `
                           -JobGroup $GUID.Guid;
$networkAdapter = @();
$networkAdapter += Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `
                                              -VMHost S2D02;

$logicalSwitch = Get-SCLogicalSwitch -Name Prod;

New-SCVirtualNetwork -VMHost $vmHost `
                     -VMHostNetworkAdapters $networkAdapter `
                     -LogicalSwitch $logicalSwitch `
                     -DeployVirtualNetworkAdapters `
                     -JobGroup $GUID.Guid;


Set-SCVMHost -VMHost $vmHost `
             -JobGroup $GUID.Guid `
             -RunAsynchronously;


# Set S2D03
# Generate Job GUID
$GUID = [guid]::NewGuid();

# Get Host 'S2D03.dms.int'
$vmHost = Get-SCVMHost -ComputerName S2D03;

# Get Host Network Adapter 'Microsoft Hyper-V Network Adapter'
$networkAdapter = Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `
                                             -VMHost S2D03;

$uplinkPortProfileSet = Get-SCUplinkPortProfileSet -Name ProdUplink;

Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $networkAdapter `
                           -UplinkPortProfileSet $uplinkPortProfileSet `
                           -JobGroup $GUID.Guid;
$networkAdapter = @();
$networkAdapter += Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `
                                              -VMHost S2D03;

$logicalSwitch = Get-SCLogicalSwitch -Name Prod;

New-SCVirtualNetwork -VMHost $vmHost `
                     -VMHostNetworkAdapters $networkAdapter `
                     -LogicalSwitch $logicalSwitch `
                     -DeployVirtualNetworkAdapters `
                     -JobGroup $GUID.Guid;


Set-SCVMHost -VMHost $vmHost `
             -JobGroup $GUID.Guid `
             -RunAsynchronously;


# Set S2D04
# Generate Job GUID
$GUID = [guid]::NewGuid();

# Get Host 'S2D04.dms.int'
$vmHost = Get-SCVMHost -ComputerName S2D04;

# Get Host Network Adapter 'Microsoft Hyper-V Network Adapter'
$networkAdapter = Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `
                                             -VMHost S2D04;

$uplinkPortProfileSet = Get-SCUplinkPortProfileSet -Name ProdUplink;

Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $networkAdapter `
                           -UplinkPortProfileSet $uplinkPortProfileSet `
                           -JobGroup $GUID.Guid;
$networkAdapter = @();
$networkAdapter += Get-SCVMHostNetworkAdapter -Name "Microsoft Hyper-V Network Adapter" `
                                              -VMHost S2D04;

$logicalSwitch = Get-SCLogicalSwitch -Name Prod;

New-SCVirtualNetwork -VMHost $vmHost `
                     -VMHostNetworkAdapters $networkAdapter `
                     -LogicalSwitch $logicalSwitch `
                     -DeployVirtualNetworkAdapters `
                     -JobGroup $GUID.Guid;


Set-SCVMHost -VMHost $vmHost `
             -JobGroup $GUID.Guid `
             -RunAsynchronously;


# Set Cluster reserve to 0
$hostCluster = Get-SCVMHostCluster -Name S2D-CLU;
Set-SCVMHostCluster -VMHostCluster $hostCluster `
                    -ClusterReserve "0";
#endregion

#region Add Hyper-converged S2D Cluster Under Management
$runAsAccount = Get-SCRunAsAccount -Name "Hyper-V Hosts Admin";
# Get Host Group 'All Hosts'
$hostGroup = Get-SCVMHostGroup -Name "All Hosts";
Add-SCVMHostCluster -Name "S2D-CLU.dms.int" `
                    -RunAsynchronously `
                    -VMHostGroup $hostGroup `
                    -Credential $runAsAccount `
                    -RemoteConnectEnabled $true;
#endregion

#region Add Hyper-Converged S2D Cluster as Storage Provider
# Generate Job GUID
$GUID = [guid]::NewGuid();

$runAsAccount = Get-SCRunAsAccount -Name "Hyper-V Hosts Admin";

Add-SCStorageProvider -ComputerName "S2D-CLU.dms.int" `
                      -AddWindowsNativeWmiProvider `
                      -Name "S2D-CLU.dms.int" `
                      -RunAsAccount $runAsAccount `
                      -RunAsynchronously;


$provider = Get-SCStorageProvider -Name "S2D-CLU";
Set-SCStorageProvider -StorageProvider $provider `
                      -JobGroup $GUID.Guid `
                      -RunAsynchronously;

#endregion

#region Create and Add Storage Classification
New-SCStorageClassification -Name "Gold" `
                            -Description "" `
                            -RunAsynchronously;


$array = Get-SCStorageArray -Name "Clustered Windows Storage on S2D-CLU";
$enabledPools = @();
$enabledPools += Get-SCStoragePool -Name "S2D on S2D-CLU";
$classifications = @();
$classifications += Get-SCStorageClassification -Name Gold;
$hostGroups = @();
$hostGroups += $null;
Set-SCStorageArray -RunAsynchronously `
                   -StorageArray $array `
                   -AddStoragePoolToManagement $enabledPools `
                   -StorageClassificationAssociation $classifications `
                   -VMHostGroupAssociation $hostGroups;


#endregion

#region Create CSV
# Generate Job GUID
$GUID = [guid]::NewGuid();

$storageArray = Get-SCStorageArray -Name "Clustered Windows Storage on S2D-CLU";
$storagePool =  Get-SCStoragePool -Name "S2D on S2D-CLU";
New-SCStorageTier -StorageTierFriendlyName "Capacity" `
                  -StorageTierSizeInMB 102400 `
                  -RunAsynchronously `
                  -JobGroup $GUID.Guid;
New-SCStorageTier -StorageTierFriendlyName "Performance" `
                  -StorageTierSizeInMB 409600 `
                  -RunAsynchronously `
                  -JobGroup $GUID.Guid;
New-SCStorageVolume -StorageArray $storageArray `
                    -StoragePool $storagePool `
                    -Name "Volume1" `
                    -RunAsynchronously `
                    -JobGroup $GUID.Guid `
                    -FileSystem "CSVFS_ReFS";
#endregion

2.3.2 - Setup Software Defined Networking.ps1

#region Copy SDN Setup Files

#endregion

#region Add Windows Server 2016 Image to VMM Library
$libraryObject = Get-SCVirtualHardDisk -Name WS2016DC_disk_1.vhdx;
# Get Operating System 'Windows Server 2016 Datacenter'
$os = Get-SCOperatingSystem | where {$_.Name -eq "Windows Server 2016 Datacenter"};
Set-SCVirtualHardDisk -VirtualHardDisk $libraryObject `
                      -OperatingSystem $os `
                      -VirtualizationPlatform "HyperV" `
                      -Name "WS2016DC_disk_1.vhdx" `
                      -Description "" `
                      -Release "" `
                      -FamilyName ""; 

#endregion

#region Generate SSL Certificate for Network Controller
$NetworkControllerFQDN = "NCE-NCVM01.dms.int"
$ServerCertificatePassword = "!!Password"
$generatedCert = New-SelfSignedCertificate -KeyUsageProperty All `
                                           -Provider "Microsoft Strong cryptographic provider" `
                                           -FriendlyName "NC certificate" `
                                           -DnsName $NetworkControllerFQDN;

$certPassword = ConvertTO-SecureString -String $ServerCertificatePassword `
                                       -Force `
                                       -AsPlainText;

$certPath = "cert:\LocalMachine\My\" + $generatedCert.Thumbprint;

#The File path parameter should be path of downloaded service template servercertificate.cr folder for NC
$Exportedcert = Export-pfxCertificate  -Cert $certPath  `
                                       -FilePath "C:\VMM\Templates\NC\ServerCertificate.cr\ServerCert.pfx" `
                                       -Password $certPassword;
	
#Export the cert for SLB
$Exportedcert = Export-Certificate -Cert $certPath  `
                                   -FilePath "C:\VMM\Templates\SLB\NCCertificate.cr\MCCert.cer"; 
#endregion

#region Import Network Controller Service Template
$serviceTemplateLocation = "C:\VMM\Templates\NC\";
$PackagePath = "C:\VMM\Templates\NC\Network Controller Standalone Generation 2 VM.xml";
$VHDName = "WS2016DC_disk_1.vhdx";
#Get the package
$package = Get-SCTemplatePackage -Path $PackagePath;
	
#Get the package mapping
$allMappings = New-SCPackageMapping -TemplatePackage $package;

$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"};

$resource = Get-SCVirtualHardDisk -Name $VHDName;
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;

$VMMLibrary = Get-SCLibraryShare;
$NCsetupPath = $serviceTemplateLocation + "NCSetup.cr\";
Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `
                                 -SharePath $VMMLibrary[0] `
                                 -OverwriteExistingFiles;

$mapping = $allMappings | where {$_.PackageId -eq "NCSetup.cr"};
$resource = Get-SCCustomResource -Name "NCSetup.cr";
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;

#MAP ServerCertificate.cr
$NCsetupPath = $serviceTemplateLocation + "ServerCertificate.cr\";
Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `
                                 -SharePath $VMMLibrary[0] `
                                 -OverwriteExistingFiles;

$mapping = $allMappings | where {$_.PackageId -eq "ServerCertificate.cr"};
$resource = Get-SCCustomResource -Name "ServerCertificate.cr";
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;

#MAP TrustedRootCertificate.cr
$NCsetupPath = $serviceTemplateLocation + "TrustedRootCertificate.cr\";
Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `
                                 -SharePath $VMMLibrary[0] `
                                 -OverwriteExistingFiles;

$mapping = $allMappings | where {$_.PackageId -eq "TrustedRootCertificate.cr"};
$resource = Get-SCCustomResource -Name "TrustedRootCertificate.cr";
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;

$serviceTemplate = Import-SCTemplate -TemplatePackage $package `
                                     -Name "NC Deployment service Template" `
                                     -PackageMapping $allMappings `
                                     -Release "1.0" `
                                     -SettingsIncludePrivate;

$Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "NC-VM##"};
$ComputerNamePattern = "NCE-NCVM##";
Set-SCVMTemplate -Template $Template `
                 -ComputerName $ComputerNamePattern `
                 -ProductKey $ProductKey; 

#endregion

#region Create Network Controller Users and Groups

#endregion

#region Deploy Network Controller with Service Template
# The following are service settings required for configuring and
# deploying the service template imported client security Group Name
$ClientSecurityGroupName = "dms\NCUsers";

# Local Admin credentials
# The local admin user name will be .\Administrator
$LocalAdminPassword = "Password123!!";

# Management Domain Account Which will be used for NC Deployment
$ManagementDomainUserPassword = "Password!!";
$ManagementDomainUser = "dms\_NCService";

# This is the domain which NC VM will join
$ManagementDomainFDQN = "dms.int";

#Managemet Security Group Name
$ManagementSecurityGroupName = "dms\NCAdmins";

#The password for server certificate
$ServerCertificatePassword = "!!Password";

# Get the host group on which the service is to be deployed
$ServiceHostGroup = Get-SCVMHostGroup -Name "All Hosts";
	
#Get the service template
$serviceTemplate = Get-SCServiceTemplate -Name "NC Deployment service Template";
	
#Create a new service configuration
$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate `
                                            -Name "NC" `
                                            -VMHostGroup $ServiceHostGroup;

$ManagementVMNetwork = Get-SCVMNetwork -Name "Management"

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "Management" | Set-SCServiceSetting  -value $ManagementVMNetwork.ID;

#update the service configuration to apply placement. If there is any error,Lets stop
$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig;
if($ServiceUpdate.deploymenterrorlist -ne $null)
{       

	Write-Host "Placement failed for Service Deployment";
	exit -1
}

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "ClientSecurityGroup" | Set-SCServiceSetting  -value $ClientSecurityGroupName;

# Create the Local Admin Run As Account
$localAdminCredPassword = ConvertTo-SecureString -String $LocalAdminPassword `
                                                 -Force `
                                                 -AsPlainText;

$localAdminCred = New-Object System.Management.Automation.PSCredential (".\Administrator", $localAdminCredPassword);
$localAdminRAA = New-SCRunAsAccount -Name "NC_LocalAdminRAA" `
                                    -Credential $localAdminCred `
                                    -NoValidation;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "LocalAdmin"  | Set-SCServiceSetting  -value $localAdminRAA;                        
  
# Create the Network Controller Management Run As Account
$MgmtDomainCredPassword = ConvertTo-SecureString -String $ManagementDomainUserPassword `
                                                 -Force `
                                                 -AsPlainText;

$MgmtDomainCred = New-Object System.Management.Automation.PSCredential ($ManagementDomainUser, $MgmtDomainCredPassword);
$MgmtAdminRAA = New-SCRunAsAccount -Name "NC_MgmtAdminRAA" `
                                   -Credential $MgmtDomainCred;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtDomainAccount" | Set-SCServiceSetting  -value $MgmtAdminRAA;
                                
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtDomainAccountName" | Set-SCServiceSetting  -value $ManagementDomainUser;

$domainpwd = ConvertTo-SecureString -String $ManagementDomainUserPassword `
                                    -Force `
                                    -AsPlainText;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtDomainAccountPassword" | Set-SCServiceSetting -Securevalue $domainpwd;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtDomainFQDN" | Set-SCServiceSetting  -value $ManagementDomainFDQN;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtSecurityGroup" | Set-SCServiceSetting  -value $ManagementSecurityGroupName;

$certpassword = ConvertTo-SecureString -string $ServerCertificatePassword  `
                                       -Force `
                                       -AsPlainText;

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "ServerCertificatePassword" | Set-SCServiceSetting  -Securevalue $certpassword;

$sc = New-SCService -ServiceConfiguration $ServiceConfig; 

#endregion

#region Connect Network Controller with VMM
# NC Name
$VMName = "NCE-NCVM01.dms.int";

# Get NC Run As Management Account
$runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";

# Get NC configuration provider
$configurationProvider = Get-SCConfigurationProvider -Name "Microsoft Network Controller";

# Get the Host group NC will manage
$vmHostGroup = @()
$vmHostGroup += Get-SCVMHostGroup -Name "All Hosts";

# Get NC certificate
$certificates = @()
$certificates += Get-SCCertificate -ComputerName $VMName `
                                   -TCPPort 443;

# Construct Connection string
$ConnectionString = "serverurl=https://";
$ConnectionString += $VMName;
$ConnectionString += "/;SouthBoundIPAddress=";
$vm = Get-SCVirtualMachine -Name $VMName;
$IPv4Address = $vm.VirtualNetworkAdapters[0].IPv4Addresses;
$ConnectionString += $IPv4Address;
$ConnectionString += ";servicename=NC";

# Add NC to VMM
$NC = Add-SCNetworkService -Name "Network Controller" `
                           -RunAsAccount $runAsAccount `
                           -ConfigurationProvider $configurationProvider `
                           -VMHostGroup $vmHostGroup `
                           -ConnectionString $ConnectionString `
                           -Certificate $certificates `
                           -ProvisionSelfSignedCertificatesForNetworkService $true; 

#endregion

#region Create other logical networks for SDN
# Create HNVPA
$LogicalNetworkName = "HNVPA";
$LogicalNetworkSubnet = "10.10.56.0/24";
$LogicalNetworkVlanID = 11;
$LogicalNetworkGateway = "10.10.56.1";
$LogicalNetworkStartIp = "10.10.56.10";
$LogicalNetworkEndIp = "10.10.56.100";
$UplinkProfileName = "ProdUplink";

# Get NC
$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network
$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `
                                              -LogicalNetworkDefinitionIsolation $false `
                                              -EnableNetworkVirtualization $true `
                                              -UseGRE $true `
                                              -IsPVLAN $false `
                                              -NetworkController $NetController;

# Create Network Definition
$allHostGroups = @();
$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";
$allSubnetVLAN = @();
$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `
                                   -VLanID $LogicalNetworkVlanID;
$LNDName = $LogicalNetworkName + "_0";  
$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `
                                             -LogicalNetwork $LogicalNetworkCreated `
                                             -VMHostGroup $allHostGroups `
                                             -SubnetVLan $allSubnetVLAN;

# Create IP Pool
$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";
$allDnsServer = @();
$allGateways =@();
$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `
                                     -Automatic;
$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `
                                      -LogicalNetworkDefinition $createdLND `
                                      -Subnet $LogicalNetworkSubnet `
                                      -IPAddressRangeStart $LogicalNetworkStartIp `
                                      -IPAddressRangeEnd $LogicalNetworkEndIp `
                                      -DefaultGateway $allGateways `
                                      -DNSServer $allDnsServer;

# Add Network to Uplink profile
#Get the LogicalNetwork
$LogNet = Get-SCLogicalNetwork -Name $LogicalNetworkName;

# Get the logical Network Definition
$LogicalNetworkDefinition = Get-SCLogicalNetworkDefinition -LogicalNetwork $LogNet;
	
#Get the NC uplink port profile
$uplink = Get-SCNativeUplinkPortProfile -Name $UplinkProfileName;
	
#Set the uplink port profile
Set-SCNativeUplinkPortProfile -NativeUplinkPortProfile $uplink `
                              -AddLogicalNetworkDefinition $LogicalNetworkDefinition;






# Create Transit Logical Network
$LogicalNetworkName = "Transit";
$LogicalNetworkSubnet = "10.10.10.0/24";
$LogicalNetworkVlanID = 10;
$LogicalNetworkGateway = "10.10.10.1";
$LogicalNetworkStartIp = "10.10.10.10";
$LogicalNetworkEndIp = "10.10.10.100";
$UplinkProfileName = "ProdUplink";

# Get NC
$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network
$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `
                                              -LogicalNetworkDefinitionIsolation $false `
                                              -EnableNetworkVirtualization $false `
                                              -UseGRE $false `
                                              -IsPVLAN $false `
                                              -NetworkController $NetController;


# Create Network Definition
$allHostGroups = @();
$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";
$allSubnetVLAN = @();
$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `
                                   -VLanID $LogicalNetworkVlanID;
$LNDName = $LogicalNetworkName + "_0";  
$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `
                                             -LogicalNetwork $LogicalNetworkCreated `
                                             -VMHostGroup $allHostGroups `
                                             -SubnetVLan $allSubnetVLAN;
# Create VM Netowrk
$ManagementVMNetwork = New-SCVMNetwork -Name $LogicalNetworkName `
                                       -IsolationType "NoIsolation" `
                                       -LogicalNetwork $LogicalNetworkCreated;

# Create IP Pool
$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";
$allDnsServer = @();
$allGateways =@();
$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `
                                     -Automatic;
$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `
                                      -LogicalNetworkDefinition $createdLND `
                                      -Subnet $LogicalNetworkSubnet `
                                      -IPAddressRangeStart $LogicalNetworkStartIp `
                                      -IPAddressRangeEnd $LogicalNetworkEndIp `
                                      -DefaultGateway $allGateways `
                                      -DNSServer $allDnsServer;

# Add Network to Uplink profile
#Get the LogicalNetwork
$LogNet = Get-SCLogicalNetwork -Name $LogicalNetworkName;

# Get the logical Network Definition
$LogicalNetworkDefinition = Get-SCLogicalNetworkDefinition -LogicalNetwork $LogNet;
	
#Get the NC uplink port profile
$uplink = Get-SCNativeUplinkPortProfile -Name $UplinkProfileName;
	
#Set the uplink port profile
Set-SCNativeUplinkPortProfile -NativeUplinkPortProfile $uplink `
                              -AddLogicalNetworkDefinition $LogicalNetworkDefinition;






# Create Private VIP Logical Network
$LogicalNetworkName = "PrivateVIP";
$LogicalNetworkSubnet = "20.20.20.0/27";
$LogicalNetworkVlanID = 0;
$LogicalNetworkGateway = "20.20.20.1";
$LogicalNetworkStartIp = "20.20.20.10";
$LogicalNetworkEndIp = "20.20.20.30";


# Get NC
$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network
$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `
                                              -LogicalNetworkDefinitionIsolation $false `
                                              -EnableNetworkVirtualization $false `
                                              -UseGRE $false `
                                              -IsPVLAN $false `
                                              -NetworkController $NetController;

# Create Network Definition
$allHostGroups = @();
$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";
$allSubnetVLAN = @();
$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `
                                   -VLanID $LogicalNetworkVlanID;
$LNDName = $LogicalNetworkName + "_0";  
$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `
                                             -LogicalNetwork $LogicalNetworkCreated `
                                             -VMHostGroup $allHostGroups `
                                             -SubnetVLan $allSubnetVLAN;
# Create VM Netowrk
$ManagementVMNetwork = New-SCVMNetwork -Name $LogicalNetworkName `
                                       -IsolationType "NoIsolation" `
                                       -LogicalNetwork $LogicalNetworkCreated;

# Create IP Pool
$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";
$allDnsServer = @();
$allGateways =@();
$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `
                                     -Automatic;
$VIPAddressSet = ""
$VIPAddressSet += $LogicalNetworkStartIp
$VIPAddressSet += "-" 
$VIPAddressSet += $LogicalNetworkEndIp
$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `
                                      -LogicalNetworkDefinition $createdLND `
                                      -Subnet $LogicalNetworkSubnet `
                                      -IPAddressRangeStart $LogicalNetworkStartIp `
                                      -IPAddressRangeEnd $LogicalNetworkEndIp `
                                      -DefaultGateway $allGateways `
                                      -DNSServer $allDnsServer `
                                      -VIPAddressSet $VIPAddressSet;



# Create Public VIP logical network
$LogicalNetworkName = "PublicVIP";
$LogicalNetworkSubnet = "41.40.40.0/27";
$LogicalNetworkVlanID = 0;
$LogicalNetworkGateway = "41.40.40.1";
$LogicalNetworkStartIp = "41.40.40.10";
$LogicalNetworkEndIp = "41.40.40.30";


# Get NC
$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network
$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName  `
                                              -LogicalNetworkDefinitionIsolation $false `
                                              -EnableNetworkVirtualization $false `
                                              -UseGRE $false `
                                              -IsPVLAN $false `
                                              -NetworkController $NetController `
                                              -PublicIPNetwork;

# Create Network Definition
$allHostGroups = @();
$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";
$allSubnetVLAN = @();
$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `
                                   -VLanID $LogicalNetworkVlanID;
$LNDName = $LogicalNetworkName + "_0";  
$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `
                                             -LogicalNetwork $LogicalNetworkCreated `
                                             -VMHostGroup $allHostGroups `
                                             -SubnetVLan $allSubnetVLAN;
# Create VM Netowrk
$ManagementVMNetwork = New-SCVMNetwork -Name $LogicalNetworkName `
                                       -IsolationType "NoIsolation" `
                                       -LogicalNetwork $LogicalNetworkCreated;

# Create IP Pool
$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";
$allDnsServer = @();
$allGateways =@();
$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `
                                     -Automatic;
$VIPAddressSet = ""
$VIPAddressSet += $LogicalNetworkStartIp
$VIPAddressSet += "-" 
$VIPAddressSet += $LogicalNetworkEndIp
$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `
                                      -LogicalNetworkDefinition $createdLND `
                                      -Subnet $LogicalNetworkSubnet `
                                      -IPAddressRangeStart $LogicalNetworkStartIp `
                                      -IPAddressRangeEnd $LogicalNetworkEndIp `
                                      -DefaultGateway $allGateways `
                                      -DNSServer $allDnsServer `
                                      -VIPAddressSet $VIPAddressSet;



# Create GREVIP
$LogicalNetworkName = "GREVIP";
$LogicalNetworkSubnet = "31.30.30.0/24";
$LogicalNetworkVlanID = 0;
$LogicalNetworkGateway = "31.30.30.1";
$LogicalNetworkStartIp = "31.30.30.10";
$LogicalNetworkEndIp = "31.30.30.100";


# Get NC
$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"};

# Create Logical Network
$LogicalNetworkCreated = New-SCLogicalNetwork -Name $LogicalNetworkName `
                                              -LogicalNetworkDefinitionIsolation $false `
                                              -EnableNetworkVirtualization $false `
                                              -UseGRE $false `
                                              -IsPVLAN $false `
                                              -NetworkController $NetController;

# Create Network Definition
$allHostGroups = @();
$allHostGroups += Get-SCVMHostGroup -Name "All Hosts";
$allSubnetVLAN = @();
$allSubnetVLAN += New-SCSubnetVLAN -Subnet $LogicalNetworkSubnet `
                                   -VLanID $LogicalNetworkVlanID;
$LNDName = $LogicalNetworkName + "_0";  
$createdLND = New-SCLogicalNetworkDefinition -Name $LNDName `
                                             -LogicalNetwork $LogicalNetworkCreated `
                                             -VMHostGroup $allHostGroups `
                                             -SubnetVLan $allSubnetVLAN;
# Create VM Netowrk
$ManagementVMNetwork = New-SCVMNetwork -Name $LogicalNetworkName `
                                       -IsolationType "NoIsolation" `
                                       -LogicalNetwork $LogicalNetworkCreated;

# Create IP Pool
$IPAddressPoolName = $LogicalNetworkName + "_IPAddressPool_0";
$allDnsServer = @();
$allGateways =@();
$allGateways += New-SCDefaultGateway -IPAddress $LogicalNetworkGateway `
                                     -Automatic;
$VIPAddressSet = ""
$VIPAddressSet += $LogicalNetworkStartIp
$VIPAddressSet += "-" 
$VIPAddressSet += $LogicalNetworkEndIp
$staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName `
                                      -LogicalNetworkDefinition $createdLND `
                                      -Subnet $LogicalNetworkSubnet `
                                      -IPAddressRangeStart $LogicalNetworkStartIp `
                                      -IPAddressRangeEnd $LogicalNetworkEndIp `
                                      -DefaultGateway $allGateways `
                                      -DNSServer $allDnsServer `
                                      -VIPAddressSet $VIPAddressSet;

#endregion

#region Import Software Load Balancer Service Template
$serviceTemplateLocation = "C:\VMM\Templates\SLB\";
$PackagePath = "C:\VMM\Templates\SLB\SLB Production Generation 2 VM.xml";
$VHDName = "WS2016DC_disk_1.vhdx";
$ProductKey="";
	
#Get the package
$package = Get-SCTemplatePackage -Path $PackagePath;
	
#Get the package mapping
$allMappings = New-SCPackageMapping -TemplatePackage $package;
	
#start mapping the resources
	
#MAP the VHD
$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"};

$resource = Get-SCVirtualHardDisk -Name $VHDName;
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;
	
#MAP NCsetup.cr
$VMMLibrary = Get-SCLibraryShare;
$NCsetupPath = $serviceTemplateLocation + "\NCCertificate.cr\";
Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `
                                 -SharePath $VMMLibrary[0] `
                                 -OverwriteExistingFiles;
	
$mapping = $allMappings | where {$_.PackageId -eq "NCCertificate.cr"};
$resource = Get-SCCustomResource -Name "NCCertificate.cr";
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;
	
#MAP ServerCertificate.cr
$NCsetupPath = $serviceTemplateLocation + "\EdgeDeployment.cr\";
Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath `
                                 -SharePath $VMMLibrary[0] `
                                 -OverwriteExistingFiles;

$mapping = $allMappings | where {$_.PackageId -eq "EdgeDeployment.cr"};
$resource = Get-SCCustomResource -Name "EdgeDeployment.cr";
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;
	
#Import the service TemplatePackage
$serviceTemplate = Import-SCTemplate -TemplatePackage $package `
                                     -Name "SLB Deployment service Template" `
                                     -PackageMapping $allMappings `
                                     -Release "1.0" `
                                     -SettingsIncludePrivate;
	
$Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "muxvm###"};
$ComputerNamePattern = "NCE-muxvm##";
Set-SCVMTemplate -Template $Template `
                 -ComputerName $ComputerNamePattern `
                 -ProductKey $ProductKey;

#endregion

#region Deploy Software Load Balancer with Service Template

$ManagementDomainFDQN = "dms.int";	
# Get the host group on which the service is to be deployed
$ServiceHostGroup = Get-SCVMHostGroup -Name "All Hosts";
	
#Get the service template
$serviceTemplate = Get-SCServiceTemplate -Name "SLB Deployment service Template";

#Resolve the service Template
Resolve-SCServiceTemplate -ServiceTemplate $serviceTemplate `
                          -update;

	
#Create a new service configuration
$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate `
                                            -Name "Software Load Balancer" `
                                            -VMHostGroup $ServiceHostGroup;

# Set Management Network	
$ManagementNetwork = Get-SCVMNetwork -Name "Management";

Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "ManagementNetwork" | Set-SCServiceSetting  -value $ManagementNetwork.ID;
    
# Set Transit Network
$TransitNetwork = Get-SCVMNetwork -Name "Transit";
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "TransitNetwork" | Set-SCServiceSetting  -value $TransitNetwork.ID;
    

#update the service configuration to apply placement. If there is any error,Lets stop
$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig;
if($ServiceUpdate.deploymenterrorlist -ne $null)
{       
    Write-Host "Placement failed for Service Deployment";
	exit -1
};
	
#set the service template settings
      
# Create the Local Admin Run As Account
    
$localAdminRAA = Get-SCRunAsAccount -Name "NC_LocalAdminRAA" ;
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "LocalAdmin" |Set-SCServiceSetting -value $localAdminRAA;                      
  
# Create the Local Admin Run As Account
$MgmtAdminRAA = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtDomainAccount" | Set-SCServiceSetting  -value $MgmtAdminRAA;         
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtDomainFQDN" | Set-SCServiceSetting  -value $ManagementDomainFDQN;
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "SelfSignedConfiguration" | Set-SCServiceSetting  -value $true;
	
#create Instance of the service
$sc= New-SCService -ServiceConfiguration $ServiceConfig;

#endregion

#region Connect SLB to Network Controller

$networkService = Get-SCNetworkService -Name "Network Controller";
	
$fabricRole = Get-SCFabricRole -NetworkService $networkService | where {$_.RoleType -eq "LoadBalancer"};
	
#get the last IP address of Private VIP
$ippool = Get-SCStaticIPAddressPool -Name "PrivateVIP_IPAddressPool_0";
$LBManagerIPAddress = $ippool.IPAddressRangeEnd;
	
$vipPools = @();
$vipPools += Get-SCStaticIPAddressPool -Name "PrivateVIP_IPAddressPool_0";
$vipPools += Get-SCStaticIPAddressPool -Name "PublicVIP_IPAddressPool_0";
$natIPExemptions = @();
	
$fabricRoleConfiguration = New-SCLoadBalancerRoleConfiguration -LBManagerIPAddress $LBManagerIPAddress `
                                                               -NatIPExemptions $natIPExemptions `
                                                               -VipPools $vipPools;
	
$fabricRole = Set-SCFabricRole -FabricRole $fabricRole `
                               -LoadBalancerConfiguration $fabricRoleConfiguration;
	
# Get Service Instance 'SLB'
$service = Get-SCService -Name "Software Load Balancer";
# Get RunAs Account 'NC_MgmtAdminRAA'
$runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";

Add-SCFabricRoleResource -FabricRole $fabricRole `
                         -ServiceInstance $service `
                         -RunAsAccount $runAsAccount;


# Add BGP Settings
# Mux 1
$LoadBalancerName = "NCE-muxvm01.dms.int";
# Get Network Service
$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Load Balancer
$fabricRole = Get-SCFabricRole -Type LoadBalancer `
                               -NetworkService $networkService;

# Configure BGP
$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq $LoadBalancerName };
$bgpPeers = @()
$bgpPeers += New-SCNCBGPPeer -RouterName "BGP Peer" `
                             -RouterIPAddress "10.10.10.1" `
                             -RouterAsn 64623;
$bgpRouter = New-SCNCBGPRouter -LocalASN 64628 `
                               -RouterPeers $bgpPeers;
Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `
                         -NCBGPRouter $bgpRouter;

# Mux 2
$LoadBalancerName = "NCE-muxvm02.dms.int";
# Get Network Service
$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Load Balancer
$fabricRole = Get-SCFabricRole -Type LoadBalancer `
                               -NetworkService $networkService;

# Configure BGP
$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq $LoadBalancerName };
$bgpPeers = @()
$bgpPeers += New-SCNCBGPPeer -RouterName "BGP Peer" `
                             -RouterIPAddress "10.10.10.1" `
                             -RouterAsn 64623;
$bgpRouter = New-SCNCBGPRouter -LocalASN 64628 `
                               -RouterPeers $bgpPeers;
Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `
                         -NCBGPRouter $bgpRouter;

# Mux 3
$LoadBalancerName = "NCE-muxvm02.dms.int";
# Get Network Service
$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Load Balancer
$fabricRole = Get-SCFabricRole -Type LoadBalancer `
                               -NetworkService $networkService;

# Configure BGP
$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq $LoadBalancerName };
$bgpPeers = @()
$bgpPeers += New-SCNCBGPPeer -RouterName "BGP Peer" `
                             -RouterIPAddress "10.10.10.1" `
                             -RouterAsn 64623;
$bgpRouter = New-SCNCBGPRouter -LocalASN 64628 `
                               -RouterPeers $bgpPeers;
Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `
                         -NCBGPRouter $bgpRouter;
#endregion

#region Create and configure BGP Router Setting for SLB on RRAS VM

# Create BGP Router
Add-BgpRouter -BgpIdentifier 10.10.10.1 `
              -LocalASN 64623;
# Add BGP Peers
$Mux001IP = "10.10.10.10"
Add-BgpPeer -Name Mux001 `
            -LocalIPAddress 10.10.10.1 `
            -PeerIPAddress $Mux001IP `
            -LocalASN 64623 `
            -PeerASN 64628 `
            -OperationMode Mixed `
            -PeeringMode Automatic;

$Mux002IP = "10.10.10.11"
Add-BgpPeer -Name Mux002 `
            -LocalIPAddress 10.10.10.1 `
            -PeerIPAddress $Mux002IP `
            -LocalASN 64623 `
            -PeerASN 64628 `
            -OperationMode Mixed `
            -PeeringMode Automatic;

$Mux003IP = "10.10.10.12"
Add-BgpPeer -Name Mux003 `
            -LocalIPAddress 10.10.10.1 `
            -PeerIPAddress $Mux003IP `
            -LocalASN 64623 `
            -PeerASN 64628 `
            -OperationMode Mixed `
            -PeeringMode Automatic;

Get-BgpPeer;
#endregion

#region Import RAS Gateway Service Template
$serviceTemplateLocation = "C:\VMM\Templates\GW\";
$PackagePath = "C:\VMM\Templates\GW\EdgeServiceTemplate_Generation2.xml";
$VHDName = "WS2016DC_disk_1.vhdx";
$ProductKey="";

	
#Get the package
$package = Get-SCTemplatePackage -Path $PackagePath;
	
#Get the package mapping
$allMappings = New-SCPackageMapping -TemplatePackage $package;
	
#start mapping the resources
	
#MAP the VHD
$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"};

$resource = Get-SCVirtualHardDisk -Name $VHDName;
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;
	
$mapping = $allMappings | where {$_.PackageId -eq "NCCertificate.cr"};
$resource = Get-SCCustomResource -Name "NCCertificate.cr";
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;

$mapping = $allMappings | where {$_.PackageId -eq "EdgeDeployment.cr"};
$resource = Get-SCCustomResource -Name "EdgeDeployment.cr";
Set-SCPackageMapping -PackageMapping $mapping `
                     -TargetObject $resource;
	
#Import the service TemplatePackage
$serviceTemplate = Import-SCTemplate -TemplatePackage $package `
                                     -Name "Gateway Deployment service Template" `
                                     -PackageMapping $allMappings `
                                     -Release "1.0" `
                                     -SettingsIncludePrivate;
	
$Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "GW-VM###"};
$ComputerNamePattern =  "NCE-GW-VM##";
Set-SCVMTemplate -Template $Template `
                 -ComputerName $ComputerNamePattern `
                 -ProductKey $ProductKey;
#endregion

#region Deploy RAS Gateway with Service Template
$ManagementDomainFDQN = "dms.int";
# Get the host group on which the service is to be deployed
$ServiceHostGroup = Get-SCVMHostGroup -Name "All Hosts";
	
#Get the service template
$serviceTemplate = Get-SCServiceTemplate -Name "Gateway Deployment service Template";

#Resolve the service Template
Resolve-SCServiceTemplate -ServiceTemplate $serviceTemplate `
                          -update;

#Create a new service configuration
$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate `
                                            -Name "Gateway Manager" `
                                            -VMHostGroup $ServiceHostGroup;

	
# Set Management Network
$ManagementNetwork = Get-SCVMNetwork -Name "Management";

    
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "ManagementNetwork" | Set-SCServiceSetting -value $ManagementNetwork.ID;
        

#update the service configuration to apply placement. If there is any error,Lets stop
$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig;
if($ServiceUpdate.deploymenterrorlist -ne $null)
{       

	Write-Host "Placement failed for Service Deployment";
	exit -1
};
	
#set the service template settings
      
# Create the Local Admin Run As Account
    
$localAdminRAA = Get-SCRunAsAccount -Name "NC_LocalAdminRAA";
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "AdminAccount" | Set-SCServiceSetting  -value $localAdminRAA;                     
  
# Create the Local Admin Run As Account
$MgmtAdminRAA = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtDomainAccount" | Set-SCServiceSetting -value $MgmtAdminRAA;           
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "MgmtDomainFQDN" |Set-SCServiceSetting -value $ManagementDomainFDQN;
Get-SCServiceSetting -ServiceConfiguration $ServiceConfig `
                     -Name "SelfSignedConfiguration" | Set-SCServiceSetting -value $true;


#create Instance of the service
$sc= New-SCService -ServiceConfiguration $ServiceConfig;


# Add _NCService account to local Administrators
Enter-PSSession nce-gw-vm01
Net localgroup Administrators "dms\_NCService" /add
exit
Enter-PSSession nce-gw-vm02
Net localgroup Administrators "dms\_NCService" /add
exit
Enter-PSSession nce-gw-vm03
Net localgroup Administrators "dms\_NCService" /add
exit
#endregion

#region Connect RAS Gateway to Network Controller
$networkService = Get-SCNetworkService -Name "Network Controller";
	
$fabricRole = Get-SCFabricRole -NetworkService $networkService | where {$_.RoleType -eq "Gateway"};
	
#get the last IP address of Private VIP
$GREVIP = get-SCLogicalNetworkDefinition -Name "GREVIP_0";
$subnetVlansGreVip = @();
$subnetVlanGreVipIPv4 = New-SCSubnetVLan -Subnet $GREVIP[0].SubnetVLans[0].Subnet `
                                         -VLanID $GREVIP[0].SubnetVLans[0].VLanID;

$subnetVlansGreVip += $subnetVlanGreVipIPv4;
    
$ippool = Get-SCStaticIPAddressPool -Name "PublicVIP_IPAddressPool_0";
$publicIPV4Address = Grant-SCIPAddress -PublicIPAddress `
                                       -NetworkController $networkController `
                                       -IPAddress $ippool.IPAddressRangeEnd;
$publicIPAddresses = @();
$publicIPAddresses += $publicIPV4Address;

$fabricRoleConfiguration = New-SCGatewayRoleConfiguration -GatewayCapacityKbps 1024000 `
                                                          -PublicIPAddresses $publicIPAddresses `
                                                          -RedundantResourceCount 0 `
                                                          -GreVipSubnets $subnetVlansGreVip;
$fabricRole = Set-SCFabricRole -FabricRole $fabricRole `
                               -GatewayConfiguration $fabricRoleConfiguration;

# Get Service Instance 'SLB'
$service = Get-SCService -Name "Gateway Manager";
# Get RunAs Account 'NC_MgmtAdminRAA'
$runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA";
$compTier = Get-SCComputerTier -Service $service;
	
$Transit = get-SCLogicalNetworkDefinition -Name "Transit_0";
$subnetVlanIPv4 = New-SCSubnetVLan -Subnet $Transit.SubnetVLans[0].Subnet `
                                   -VLanID $Transit.SubnetVLans[0].VLanID;
	
foreach ($VM in $compTier.VMs)
{
    $vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Resource -eq $VM };
	Add-SCFabricRoleResource -FabricRole $fabricRole `
                             -VirtualMachine $VM `
                             -IPv4Subnet $subnetVlanIPv4 `
                             -RunAsAccount $runAsAccount;
};


# Add BGP
# GW1
$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Gateway
$fabricRole = Get-SCFabricRole -Type Gateway `
                               -NetworkService $networkService;

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq "NCE-GW-VM01.dms.int" };
$bgpPeers = @();
$bgpPeers += New-SCNCBGPPeer -RouterName "gatewaypeer" `
                             -RouterIPAddress "10.10.10.1" `
                             -RouterAsn 64623;
$bgpRouter = New-SCNCBGPRouter -LocalASN 65000 `
                               -RouterPeers $bgpPeers;
Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `
                          -NCBGPRouter $bgpRouter;

# GW2
$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Gateway
$fabricRole = Get-SCFabricRole -Type Gateway `
                               -NetworkService $networkService;

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq "NCE-GW-VM02.dms.int" };
$bgpPeers = @();
$bgpPeers += New-SCNCBGPPeer -RouterName "gatewaypeer" `
                             -RouterIPAddress "10.10.10.1" `
                             -RouterAsn 64623;
$bgpRouter = New-SCNCBGPRouter -LocalASN 65000 `
                               -RouterPeers $bgpPeers;
Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `
                          -NCBGPRouter $bgpRouter;


# GW3
$networkService = Get-SCNetworkService -Name "Network Controller";

# Get Fabric role Gateway
$fabricRole = Get-SCFabricRole -Type Gateway `
                               -NetworkService $networkService;

$vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Name -eq "NCE-GW-VM03.dms.int" };
$bgpPeers = @();
$bgpPeers += New-SCNCBGPPeer -RouterName "gatewaypeer" `
                             -RouterIPAddress "10.10.10.1" `
                             -RouterAsn 64623;
$bgpRouter = New-SCNCBGPRouter -LocalASN 65000 `
                               -RouterPeers $bgpPeers;
Set-SCFabricRoleResource -FabricRoleResource $vmFabricRoleResource `
                          -NCBGPRouter $bgpRouter;
#endregion

#region Configure BGP Router Setting for RAS Gateway on RRAS VM
$GW001IP = "10.10.10.14"
Add-BgpPeer -Name GW001 `
            -LocalIPAddress 10.10.10.1 `
            -PeerIPAddress $GW001IP `
            -LocalASN 64623 `
            -PeerASN 65000 `
            -OperationMode Mixed `
            -PeeringMode Automatic;

$GW002IP = "10.10.10.13"
Add-BgpPeer -Name GW002 `
            -LocalIPAddress 10.10.10.1 `
            -PeerIPAddress $GW002IP `
            -LocalASN 64623 `
            -PeerASN 65000 `
            -OperationMode Mixed `
            -PeeringMode Automatic;

$GW003IP = "10.10.10.15"
Add-BgpPeer -Name GW003 `
            -LocalIPAddress 10.10.10.1 `
            -PeerIPAddress $GW003IP `
            -LocalASN 64623 `
            -PeerASN 65000 `
            -OperationMode Mixed `
            -PeeringMode Automatic;

Get-BgpPeer;
#endregion

2.4.1 - Create and Test SDN Tenant Nework.ps1

#region Create Virtual Network
# Create Tenant Network
# Get HNVPA logical Network
$logicalNetwork = Get-SCLogicalNetwork -Name "HNVPA";

# Create new Network
$vmNetwork = New-SCVMNetwork -Name "TenantA" `
                             -LogicalNetwork $logicalNetwork `
                             -IsolationType "WindowsNetworkVirtualization" `
                             -CAIPAddressPoolType "IPV4" `
                             -PAIPAddressPoolType "IPV4";
Write-Output $vmNetwork;

# Create Subnet for the Network
$subnet = New-SCSubnetVLan -Subnet "192.168.1.0/24";
New-SCVMSubnet -Name "Subnet1" `
               -VMNetwork $vmNetwork `
               -SubnetVLan $subnet;

# Get VMSubnet 'Subnet1'
$vmSubnet = Get-SCVMSubnet -Name "Subnet1" `
                           -VMNetwork $vmNetwork;

# Create IP Pool for the network
# Gateways
$allGateways = @();

# DNS servers
$allDnsServer = @();

# DNS suffixes
$allDnsSuffixes = @();

# WINS servers
$allWinsServers = @();

New-SCStaticIPAddressPool -Name "Pool1" `
                          -VMSubnet $vmSubnet `
                          -Subnet "192.168.1.0/24" `
                          -IPAddressRangeStart "192.168.1.4" `
                          -IPAddressRangeEnd "192.168.1.254" `
                          -DefaultGateway $allGateways `
                          -DNSServer $allDnsServer `
                          -DNSSuffix "" `
                          -DNSSearchSuffix $allDnsSuffixes;

#endregion

#region Create VM01
# Create Job Guids
$GUIDJob1 = [guid]::NewGuid();
$GUIDJob2 = [guid]::NewGuid();
$HWProfileName = "Profile" + [guid]::NewGuid().Guid
$SCTemplateName = "Temporary Template" + [guid]::NewGuid().Guid
# Create scsi adapter
New-SCVirtualScsiAdapter -JobGroup $GUIDJob1.Guid `
                         -AdapterID 7 `
                         -ShareVirtualScsiAdapter $false `
                         -ScsiControllerType DefaultTypeNoType;

# Create virtual dvd drive
New-SCVirtualDVDDrive -JobGroup $GUIDJob1.Guid `
                      -Bus 0 `
                      -LUN 1;

# Get VM Network and Subnet
$VMNetwork = Get-SCVMNetwork  -Name "TenantA";
$VMSubnet = Get-SCVMSubnet -Name "Subnet1" `
                           -VMNetwork $VMNetwork;

# Get Port Classificaiton
$PortClassification = Get-SCPortClassification -Name "Medium bandwidth";

# Create virtual network adapter
New-SCVirtualNetworkAdapter -JobGroup $GUIDJob1.Guid `
                            -MACAddress "00:00:00:00:00:00" `
                            -MACAddressType Static `
                            -Synthetic `
                            -IPv4AddressType Dynamic `
                            -IPv6AddressType Dynamic `
                            -VMSubnet $VMSubnet `
                            -VMNetwork $VMNetwork `
                            -PortClassification $PortClassification;

# Get CPU type
$CPUType = Get-SCCPUType | where {$_.Name -eq "3.60 GHz Xeon (2 MB L2 cache)"};

# Create new HW profile
New-SCHardwareProfile -CPUType $CPUType `
                      -Name $HWProfileName `
                      -Description "Profile used to create a VM/Template" `
                      -CPUCount 2 `
                      -MemoryMB 2048 `
                      -DynamicMemoryEnabled $false `
                      -MemoryWeight 5000 `
                      -CPUExpectedUtilizationPercent 20 `
                      -DiskIops 0 `
                      -CPUMaximumPercent 100 `
                      -CPUReserve 0 `
                      -NumaIsolationRequired $false `
                      -NetworkUtilizationMbps 0 `
                      -CPURelativeWeight 100 `
                      -HighlyAvailable $true `
                      -HAVMPriority 2000 `
                      -DRProtectionRequired $false `
                      -SecureBootEnabled $true `
                      -SecureBootTemplate "MicrosoftWindows" `
                      -CPULimitFunctionality $false `
                      -CPULimitForMigration $false `
                      -CheckpointType Production `
                      -Generation 2 `
                      -JobGroup $GUIDJob1.Guid;


# Get OS VHD
$VirtualHardDisk = Get-SCVirtualHardDisk -Name "WS2016DC_disk_1.vhdx";

# Create Virtual Disk Drive
New-SCVirtualDiskDrive -SCSI `
                       -Bus 0 `
                       -LUN 0 `
                       -JobGroup $GUIDJob2.Guid `
                       -CreateDiffDisk $false `
                       -VirtualHardDisk $VirtualHardDisk `
                       -FileName "VM01_WS2016DC_disk_1.vhdx" `
                       -VolumeType BootAndSystem ;

# Get HW profile
$HardwareProfile = Get-SCHardwareProfile | where {$_.Name -eq $HWProfileName }

# Create new VM Template
New-SCVMTemplate -Name $SCTemplateName `
                 -Generation 2 `
                 -HardwareProfile $HardwareProfile `
                 -JobGroup $GUIDJob2.Guid `
                 -NoCustomization;


# Get VM Template and VM configuration
$template = Get-SCVMTemplate -All | where { $_.Name -eq $SCTemplateName };
$virtualMachineConfiguration = New-SCVMConfiguration -VMTemplate $template `
                                                     -Name "VM01";
Write-Output $virtualMachineConfiguration;

# Get Host
$vmHost = Get-SCVMHost -ComputerName S2D01;

# Set VM configuration
Set-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration `
                      -VMHost $vmHost;
Update-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration;
Set-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration `
                      -VMLocation "C:\ClusterStorage\Volume1\" `
                      -PinVMLocation $true;

$AllNICConfigurations = Get-SCVirtualNetworkAdapterConfiguration -VMConfiguration $virtualMachineConfiguration;
$VHDConfiguration = Get-SCVirtualHardDiskConfiguration -VMConfiguration $virtualMachineConfiguration;
Set-SCVirtualHardDiskConfiguration -VHDConfiguration $VHDConfiguration `
                                   -PinSourceLocation $false `
                                   -PinDestinationLocation $false `
                                   -PinFileName $false `
                                   -StorageQoSPolicy $null `
                                   -DeploymentOption "UseNetwork";



Update-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration;
$operatingSystem = Get-SCOperatingSystem | where { $_.Name -eq "Windows Server 2016 Datacenter" };

# Deploy VM
New-SCVirtualMachine -Name "VM01" `
                     -VMConfiguration $virtualMachineConfiguration `
                     -Description "" `
                     -BlockDynamicOptimization $false `
                     -StartVM `
                     -JobGroup $GUIDJob2.Guid `
                     -ReturnImmediately `
                     -StartAction "NeverAutoTurnOnVM" `
                     -StopAction "SaveVM" `
                     -OperatingSystem $operatingSystem;
#endregion

#region Create VM02
# Create Job Guids
$GUIDJob1 = [guid]::NewGuid();
$GUIDJob2 = [guid]::NewGuid();
$HWProfileName = "Profile" + [guid]::NewGuid().Guid
$SCTemplateName = "Temporary Template" + [guid]::NewGuid().Guid
# Create scsi adapter
New-SCVirtualScsiAdapter -JobGroup $GUIDJob1.Guid `
                         -AdapterID 7 `
                         -ShareVirtualScsiAdapter $false `
                         -ScsiControllerType DefaultTypeNoType;

# Create virtual dvd drive
New-SCVirtualDVDDrive -JobGroup $GUIDJob1.Guid `
                      -Bus 0 `
                      -LUN 1;

# Get VM Network and Subnet
$VMNetwork = Get-SCVMNetwork  -Name "TenantA";
$VMSubnet = Get-SCVMSubnet -Name "Subnet1" `
                           -VMNetwork $VMNetwork;

# Get Port Classificaiton
$PortClassification = Get-SCPortClassification -Name "Medium bandwidth";

# Create virtual network adapter
New-SCVirtualNetworkAdapter -JobGroup $GUIDJob1.Guid `
                            -MACAddress "00:00:00:00:00:00" `
                            -MACAddressType Static `
                            -Synthetic `
                            -IPv4AddressType Dynamic `
                            -IPv6AddressType Dynamic `
                            -VMSubnet $VMSubnet `
                            -VMNetwork $VMNetwork `
                            -PortClassification $PortClassification;

# Get CPU type
$CPUType = Get-SCCPUType | where {$_.Name -eq "3.60 GHz Xeon (2 MB L2 cache)"};

# Create new HW profile
New-SCHardwareProfile -CPUType $CPUType `
                      -Name $HWProfileName `
                      -Description "Profile used to create a VM/Template" `
                      -CPUCount 2 `
                      -MemoryMB 2048 `
                      -DynamicMemoryEnabled $false `
                      -MemoryWeight 5000 `
                      -CPUExpectedUtilizationPercent 20 `
                      -DiskIops 0 `
                      -CPUMaximumPercent 100 `
                      -CPUReserve 0 `
                      -NumaIsolationRequired $false `
                      -NetworkUtilizationMbps 0 `
                      -CPURelativeWeight 100 `
                      -HighlyAvailable $true `
                      -HAVMPriority 2000 `
                      -DRProtectionRequired $false `
                      -SecureBootEnabled $true `
                      -SecureBootTemplate "MicrosoftWindows" `
                      -CPULimitFunctionality $false `
                      -CPULimitForMigration $false `
                      -CheckpointType Production `
                      -Generation 2 `
                      -JobGroup $GUIDJob1.Guid;


# Get OS VHD
$VirtualHardDisk = Get-SCVirtualHardDisk -Name "WS2016DC_disk_1.vhdx";

# Create Virtual Disk Drive
New-SCVirtualDiskDrive -SCSI `
                       -Bus 0 `
                       -LUN 0 `
                       -JobGroup $GUIDJob2.Guid `
                       -CreateDiffDisk $false `
                       -VirtualHardDisk $VirtualHardDisk `
                       -FileName "VM01_WS2016DC_disk_1.vhdx" `
                       -VolumeType BootAndSystem ;

# Get HW profile
$HardwareProfile = Get-SCHardwareProfile | where {$_.Name -eq $HWProfileName }

# Create new VM Template
New-SCVMTemplate -Name $SCTemplateName `
                 -Generation 2 `
                 -HardwareProfile $HardwareProfile `
                 -JobGroup $GUIDJob2.Guid `
                 -NoCustomization;


# Get VM Template and VM configuration
$template = Get-SCVMTemplate -All | where { $_.Name -eq $SCTemplateName };
$virtualMachineConfiguration = New-SCVMConfiguration -VMTemplate $template `
                                                     -Name "VM01";
Write-Output $virtualMachineConfiguration;

# Get Host
$vmHost = Get-SCVMHost -ComputerName S2D02;

# Set VM configuration
Set-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration `
                      -VMHost $vmHost;
Update-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration;
Set-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration `
                      -VMLocation "C:\ClusterStorage\Volume1\" `
                      -PinVMLocation $true;

$AllNICConfigurations = Get-SCVirtualNetworkAdapterConfiguration -VMConfiguration $virtualMachineConfiguration;
$VHDConfiguration = Get-SCVirtualHardDiskConfiguration -VMConfiguration $virtualMachineConfiguration;
Set-SCVirtualHardDiskConfiguration -VHDConfiguration $VHDConfiguration `
                                   -PinSourceLocation $false `
                                   -PinDestinationLocation $false `
                                   -PinFileName $false `
                                   -StorageQoSPolicy $null `
                                   -DeploymentOption "UseNetwork";



Update-SCVMConfiguration -VMConfiguration $virtualMachineConfiguration;
$operatingSystem = Get-SCOperatingSystem | where { $_.Name -eq "Windows Server 2016 Datacenter" };

# Deploy VM
New-SCVirtualMachine -Name "VM02" `
                     -VMConfiguration $virtualMachineConfiguration `
                     -Description "" `
                     -BlockDynamicOptimization $false `
                     -StartVM `
                     -JobGroup $GUIDJob2.Guid `
                     -ReturnImmediately `
                     -StartAction "NeverAutoTurnOnVM" `
                     -StopAction "SaveVM" `
                     -OperatingSystem $operatingSystem;
#endregion

#region Install Web Server Role on VM01 and VM02
Install-WindowsFeature -Name Web-Server;
#endregion

#region Test Network Connecitvity between VM01 and VM02
New-NetFirewallRule -DisplayName "Allow ICMPv4-In" `
                    -Protocol ICMPv4;

ping 192.168.1.8
#endregion

2.4.2 - Manage Storage QoS Policies.ps1

#region Create Aggregated Storage QoS Policy
# Get Storage Array
$storageArraysToAdd = @();
$storageArraysToAdd +=  Get-SCStorageArray -Name "Clustered Windows Storage on S2D-CLU";

# Create Policy
New-SCStorageQoSPolicy -Name "AggregatedPolicy500" `
                       -Description "" `
                       -PolicyType "Aggregated" `
                       -IOPSMinimum "400" `
                       -IOPSMaximum "500" `
                       -BandwidthLimitMBPS "0" `
                       -StorageArray $storageArraysToAdd `
                       -IOPSNormalizationSizeKB "8";
#endregion

#region Assign Aggregated QoS Policy to VM01 and VM02

# Get VM01
$VM = Get-SCVirtualMachine -Name VM01;

# Get VM01 disks
$VirtualDiskDrives = @();
$VirtualDiskDrives +=  Get-SCVirtualDiskDrive -VM $VM;

# Get Storage QoS Policy
$StorageQoSPolicy = Get-SCStorageQoSPolicy  -Name "AggregatedPolicy500";

# Assign Policy for each one of the disks
Foreach ($VirtualDiskDrive in $VirtualDiskDrives)
{
    Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `
                           -StorageQoSPolicy $StorageQoSPolicy; 
};

# Get VM02
$VM = Get-SCVirtualMachine -Name VM02;
# Get VM02 disks
$VirtualDiskDrives = @();
$VirtualDiskDrives +=  Get-SCVirtualDiskDrive -VM $VM;

# Get Storage QoS Policy
$StorageQoSPolicy = Get-SCStorageQoSPolicy  -Name "AggregatedPolicy500";

# Assign Policy for each one of the disks
Foreach ($VirtualDiskDrive in $VirtualDiskDrives)
{
    Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `
                           -StorageQoSPolicy $StorageQoSPolicy; 
};

#endregion

#region Create Dedicated Storage QoS Policy
# Get Storage Array
$storageArraysToAdd = @();
$storageArraysToAdd +=  Get-SCStorageArray -Name "Clustered Windows Storage on S2D-CLU";

# Create Policy
New-SCStorageQoSPolicy -Name "DedicatedPolicy500" `
                       -Description "" `
                       -PolicyType "Dedicated" `
                       -IOPSMinimum "400" `
                       -IOPSMaximum "500" `
                       -BandwidthLimitMBPS "0" `
                       -StorageArray $storageArraysToAdd `
                       -IOPSNormalizationSizeKB "8";

#endregion

#region Assign Dedicated QoS Policy to VM01 and VM02
# Get VM01
$VM = Get-SCVirtualMachine -Name VM01;

# Get VM01 disks
$VirtualDiskDrives = @();
$VirtualDiskDrives +=  Get-SCVirtualDiskDrive -VM $VM;

# Get Storage QoS Policy
$StorageQoSPolicy = Get-SCStorageQoSPolicy  -Name "DedicatedPolicy500";

# Assign Policy for each one of the disks
Foreach ($VirtualDiskDrive in $VirtualDiskDrives)
{
    Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `
                           -StorageQoSPolicy $StorageQoSPolicy; 
};

# Get VM02
$VM = Get-SCVirtualMachine -Name VM02;
# Get VM02 disks
$VirtualDiskDrives = @();
$VirtualDiskDrives +=  Get-SCVirtualDiskDrive -VM $VM;

# Get Storage QoS Policy
$StorageQoSPolicy = Get-SCStorageQoSPolicy  -Name "DedicatedPolicy500";

# Assign Policy for each one of the disks
Foreach ($VirtualDiskDrive in $VirtualDiskDrives)
{
    Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `
                           -StorageQoSPolicy $StorageQoSPolicy; 
};
#endregion

#region Remove Storage QoS Policy from VM01 and VM02
# Get VM01
$VM = Get-SCVirtualMachine -Name VM01;

# Get VM01 disks
$VirtualDiskDrives = @();
$VirtualDiskDrives +=  Get-SCVirtualDiskDrive -VM $VM;

# Remove Policy for each one of the disks
Foreach ($VirtualDiskDrive in $VirtualDiskDrives)
{
    Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `
                           -IOPSMaximum 0; 
};

# Get VM02
$VM = Get-SCVirtualMachine -Name VM02;
# Get VM02 disks
$VirtualDiskDrives = @();
$VirtualDiskDrives +=  Get-SCVirtualDiskDrive -VM $VM;

# Remove Policy for each one of the disks
Foreach ($VirtualDiskDrive in $VirtualDiskDrives)
{
    Set-SCVirtualDiskDrive -VirtualDiskDrive $VirtualDiskDrive `
                           -IOPSMaximum 0; 
};
#endregion

2.4.3 - Manage Firewall Rules.ps1

#region Create Network Controller Managed ACL and Rule
# Create Port ACL
$PortAcl = New-SCPortACL -Name "TenantAACL" `
                         -Description "" `
                         -ManagedByNC;
$PortAcl;
# Create Port ACL Rule
New-SCPortACLrule -PortACL $PortAcl `
                  -Name "Deny80" `
                  -Description "" `
                  -Type Inbound `
                  -Action Deny `
                  -Priority 200 `
                  -Protocol Tcp `
                  -LocalPortRange 80;


#endregion

#region Assign Network Controller Managed ACL to Network Adapter

# Assign Port ACL to vNic on VM01
Get-SCVirtualMachine -Name VM01 | `
    Get-SCVirtualNetworkAdapter | `
        Set-SCVirtualNetworkAdapter -PortACL $PortAcl;

#endregion

#region Test Network Controller Managed ACL
Test-NetConnection -ComputerName 192.168.1.8 `
                   -Port 80;

# Create Port ACL Rule
New-SCPortACLrule -PortACL $PortAcl `
                  -Name "Allow80" `
                  -Description "" `
                  -Type Inbound `
                  -Action Allow `
                  -Priority 150 `
                  -Protocol Tcp `
                  -LocalPortRange 80;

#endregion

#region Change Network Controller Managed ACL Rule
$ACLPortRule = Get-SCPortACLRule -Name "Deny80";
Set-SCPortACLrule -PortACLrule $ACLPortRule `
                  -Name "Deny80" `
                  -Priority 149;


Test-NetConnection -ComputerName 192.168.1.8 `
                   -Port 80;
#endregion

#region Remove Network Controller Managed ACL and Rule
# Remove Port AC Lfrom vNic on VM01
Get-SCVirtualMachine -Name VM01 | `
    Get-SCVirtualNetworkAdapter | `
        Set-SCVirtualNetworkAdapter –RemovePortACL;


Get-SCPortACLrule -Name "Allow80" | Remove-SCPortACLrule;
Get-SCPortACLrule -Name "Deny80" | Remove-SCPortACLrule;
Get-SCPortACL -Name "TenantAACL" | Remove-SCPortACL;
#endregion

2.4.4 - Load Balance VMs.ps1

#region Create VIP Template
$protocol = New-SCLoadBalancerProtocol -Name "TCP";
$balancingMethod = New-SCLoadBalancingMethod -Name "RoundRobin";
$monitor = @();
New-SCLoadBalancerVIPTemplate -LoadBalancerManufacturer "Microsoft" `
                              -LoadBalancerModel "Microsoft Network Controller" `
                              -LoadBalancerHealthMonitor $monitor `
                              -Name "HTTP-80-80" `
                              -Description "" `
                              -LoadBalancingMethod $balancingMethod `
                              -LoadBalancerProtocol $protocol `
                              -LoadBalancerPort 80 `
                              -LoadBalancerBackEndPort "80";



#endregion

#region Create a SLB VIP
function CreateTenantVIP {
    param(
    
    [Parameter(Mandatory=$false)]
    # Name of the Network Controller Network Service
    # This value should be the name you gave the Network Controller service
    # when you on-boarded the Network Controller to VMM
    $LBServiceName = "Network Controller",
    
    [Parameter(Mandatory=$false)]
    # Name of the VM instances to which you want to assign the VIP
    $VipMemberVMNames =  @("VM01", "VM02"),
    
    [Parameter(Mandatory=$false)]
    # VIP address you want to assign from the VIP pool.
    # Pick any VIP that falls within your VIP IP Pool range.
    $VipAddress = "41.40.40.12",
    
    [Parameter(Mandatory=$false)]
    # Name of the VIP VM Network
    $VipNetworkName = "PublicVIP",
    
    [Parameter(Mandatory=$false)]
    # The name of the VIP template you created via the VMM Console.
    $VipTemplateName = "HTTP-80-80",
    
    [Parameter(Mandatory=$false)]
    # Arbitrary but good to match the VIP you're using.
    $VipName = "TenantAVIPWebTest"
    
    )
    
    Import-Module virtualmachinemanager
    
    $lb = Get-SCLoadBalancer | where { $_.Service.Name -eq $LBServiceName};
    $vipNetwork = Get-SCVMNetwork -Name $VipNetworkName;
    
    $vipMemberNics = @();
    foreach ($vmName in $VipMemberVMNames)
    {
    $vm = Get-SCVirtualMachine -Name $vmName;
    
    $vipMemberNics += $vm.VirtualNetworkAdapters[0];
    }
    
    $existingVip = Get-SCLoadBalancerVIP -Name $VipName
    if ($existingVip -ne $null)
    {
        foreach ($mem in $existingVip.VipMembers)
        {
            $mem | Remove-SCLoadBalancerVIPMember | Out-Null;
        }
    
    $existingVip | Remove-SCLoadBalancerVIP | Out-Null;
    }
    
    $vipt = Get-SCLoadBalancerVIPTemplate -Name $VipTemplateName;
    
    $vip = New-SCLoadBalancerVIP -Name $VipName `
                                 -LoadBalancer $lb `
                                 -IPAddress $VipAddress `
                                 -LoadBalancerVIPTemplate $vipt `
                                 -FrontEndVMNetwork $vipNetwork `
                                 -BackEndVirtualNetworkAdapters $vipMemberNics;
Write-Output "Created VIP " $vip;



$vip = get-scloadbalancervip -Name $VipName;

Write-Output "VIP with members " $vip.VIPMembers;
}

CreateTenantVIP -LBServiceName "Network Controller" `
                -VipMemberVMNames @("VM01", "VM02") `
                -VipAddress "41.40.40.12" `
                -VipNetworkName "PublicVIP" `
                -VipTemplateName "HTTP-80-80" `
                -VipName "TenantAVIPWebTest";

Get-BgpRouteInformation;
#endregion

2.4.5 - Create Site to Site VPN.ps1

#region Create Run As Account for VPN Passphrase
$secpasswd = ConvertTo-SecureString "User@123" `
                                    -AsPlainText `
                                    -Force;
$PassphraseCreds = New-Object System.Management.Automation.PSCredential ("IPSEC", $secpasswd);
$runAsAccount = New-SCRunAsAccount -Credential $PassphraseCreds `
                                   -Name "IPSECACCOUNT" `
                                   -Description "" `
                                   -NoValidation;

#endregion

#region Create S2S VPN connection on VM Network
# Variables
$RemoteEndpont = "50.50.50.1"
$RemoteIPSubnet = "60.60.60.0/24"
# Get Tenant Network
$vmNetwork = Get-SCVMNetwork -Name TenantA;

# Get Tenant Subnet
$vmSubnet = Get-SCVMSubnet -Name "Subnet1" `
                           -VMNetwork $vmNetwork;

# Get NC
$gatewayDevice = Get-SCNetworkGateway -Name "Network Controller";

# Add Gateway to Tenant Network
$VmNetworkGateway = Add-SCVMNetworkGateway -Name "TenantA_Gateway" `
                                           -EnableBGP $false `
                                           -NetworkGateway $gatewayDevice `
                                           -VMNetwork $vmNetwork `
                                           -RoutingIPSubnet "10.254.254.0/29";
# Get Run As Account for Passphrase
$runAsAccount = Get-SCRunAsAccount -Name IPSECACCOUNT;

# Create VPN connection
$vpnConnection = Add-SCVPNConnection -AuthenticationMethod "PSKOnly" `
                                     -AuthenticationTransformConstants "SHA196" `
                                     -CipherTransformConstants "AES256" `
                                     -DHGroup "Group2" `
                                     -EncryptionMethod "AES256" `
                                     -IntegrityCheckMethod "SHA1" `
                                     -PFSGroup "PFS2048" `
                                     -Protocol "IKEv2" `
                                     -Name "IPSEC-VMM" `
                                     -TargetIPv4VPNAddress $RemoteEndpont `
                                     -Secret $runAsAccount `
                                     -VMNetworkGateway $VmNetworkGateway;

# Add route to VPN Connection
Add-SCNetworkRoute -IPSubnet $RemoteIPSubnet `
                   -VPNConnection $vpnConnection `
                   -VMNetworkGateway $VmNetworkGateway;

#endregion

#region Create S2S VPN Connection on Remote Server
# Set S2S VPN interface
Set-VpnS2SInterface -Name IPSecVPN `
                    -CustomPolicy `
                    -AuthenticationTransformConstants SHA196 `
                    -CipherTransformConstants AES256 `
                    -DHGroup Group2 `
                    -EncryptionMethod AES256 `
                    -IntegrityCheckMethod SHA1 `
                    -PfsGroup PFS2048;

# Add persistent route
route -p add 41.40.40.0 mask 255.255.255.0 50.50.50.2
#endregion

AddVNets.ps1

$logicalNetwork = Get-SCLogicalNetwork -ID "9c89d0d7-8e08-4022-823b-4924b7207847" # HNV Provider


$max_vnets = 25
$max_subnets = 5

foreach ($i in 1..$max_vnets)
{
   # Create VM Network
   $vnetname = "vnet$i"
   $vmNetwork = New-SCVMNetwork -Name $vnetname -LogicalNetwork $logicalNetwork -IsolationType "WindowsNetworkVirtualization" -CAIPAddressPoolType "IPV4" -PAIPAddressPoolType "IPV4"
   #Write-Output $vmNetwork
   
   foreach ($j in 1..$max_subnets)
   {
      # Create VM Subnet
      $prefix = "10.$i.$j.0/24"
      $subnet = New-SCSubnetVLan -Subnet $prefix
      $vmsubnet = New-SCVMSubnet -Name "Vnet$i-Subnet_$prefix" -VMNetwork $vmNetwork -SubnetVLan $subnet
      #Write-Output $vmsubnet

      # Create VM Subnet IP Pool
      $allGateways = @()
      $allDnsServer = @()
      $allDnsSuffixes = @()
      $allWinsServers = @()

      New-SCStaticIPAddressPool -Name "Vnet$i-Subnet_$prefix IP Pool" -VMSubnet $vmSubnet -Subnet $prefix -IPAddressRangeStart "10.$i.$j.4" -IPAddressRangeEnd "10.$i.$j.254" -DefaultGateway $allGateways -DNSServer $allDnsServer -DNSSuffix "" -DNSSearchSuffix $allDnsSuffixes -RunAsynchronously
    }
}

sleep 5

# Remove VNets
foreach ($i in 1..$max_vnets)
{
   $vmnetwork = Get-SCVMNetwork -Name "vnet$i"

   foreach ($j in 1..$max_subnets)
   {
      $prefix = "10.$i.$j.0/24"
      $vmsubnet = Get-SCVMSubnet -Name "Vnet$i-Subnet_$prefix" -VMNetwork $vmnetwork
      $ippool =  Get-SCStaticIPAddressPool -Name "Vnet$i-Subnet_$prefix IP Pool" -VMSubnet $vmsubnet
      Remove-SCStaticIPAddressPool -StaticIPAddressPool $ippool

#      $vmsubnet = Get-SCVMSubnet -Name "Subnet_$prefix"
      Remove-SCVMSubnet $vmsubnet
   }

 #  $vmnetwork = Get-SCVMNetwork -Name "vnet$i"
   Remove-SCVMNetwork $vmnetwork
}

CreateTenantVIP.ps1

param(

[Parameter(Mandatory=$false)]
# Name of the Network Controller Network Service
# This value should be the name you gave the Network Controller service
# when you on-boarded the Network Controller to VMM
$LBServiceName = "NC",

[Parameter(Mandatory=$false)]
# Name of the VM instances to which you want to assign the VIP
$VipMemberVMNames =  @("Web_VM1", "Web_VM2"),

[Parameter(Mandatory=$false)]
# VIP address you want to assign from the VIP pool.
# Pick any VIP that falls within your VIP IP Pool range.
$VipAddress = "10.127.132.34",

[Parameter(Mandatory=$false)]
# Name of the VIP VM Network
$VipNetworkName = "Public VIP",

[Parameter(Mandatory=$false)]
# The name of the VIP template you created via the VMM Console.
$VipTemplateName = "Web",

[Parameter(Mandatory=$false)]
# Arbitrary but good to match the VIP you're using.
$VipName = "TenantVIPWebTest"

)

Import-Module virtualmachinemanager

$lb = Get-scLoadBalancer | where { $_.Service.Name -eq $LBServiceName};
$vipNetwork = get-scvmnetwork -Name $VipNetworkName;

$vipMemberNics = @();
foreach ($vmName in $VipMemberVMNames)
{
$vm = get-scvirtualmachine -Name $vmName;
#    if ($vm.VirtualNetworkAdapters[0].VMNetwork.ID -ne $vipNetwork.ID)
#    {
#        $vm.VirtualNetworkAdapters[0] | set-scvirtualnetworkadapter -VMNetwork $vipNetwork;
#    }

$vipMemberNics += $vm.VirtualNetworkAdapters[0];
}

$existingVip = get-scloadbalancervip -Name $VipName
if ($existingVip -ne $null)
{
#    foreach ($mem in $existingVip.VipMembers)
#    {
#        $mem | remove-scloadbalancervipmember;
#    }

$existingVip | remove-scloadbalancervip;
}

$vipt = get-scloadbalancerviptemplate -Name $VipTemplateName;

$vip = New-SCLoadBalancerVIP -Name $VipName -LoadBalancer $lb -IPAddress $VipAddress -LoadBalancerVIPTemplate $vipt -FrontEndVMNetwork $vipNetwork -BackEndVirtualNetworkAdapters $vipMemberNics;
Write-Output "Created VIP " $vip;

$vip = get-scloadbalancervip -Name $VipName;
Write-Output "VIP with members " $vip;

Fabricconfig_Example.psd1

# This is the sample configuration file for VMM Express. All the paremeters should be
# modified according to your setup for correct deployment of VMM Express.

@{

    AllNodes = 
    @(
        @{ 
            
							
            ###########################
            #  VM Creation variables  #
            ###########################
            
            # Name of the VHD or VHDX to use for VM creation. Must Exist in the
            # VMM Library            
            VHDName="TP5_14300_server.vhd"
            
            # Product key Can be blank if using a volume license VHD or VHDX, or you are
            #deploying in eval mode.  (Don't forget to press "skip" while VM creation).
            ProductKey="CNK65-Y6BBY-HCDPX-RVY2M-JK7JJ" 

            #Generation of VM to be used for deployment : Gen1 or Gen2
            Generation="Gen2"

            #Type of Deployment. The values are :
            #Standalone : For single Node 
            #Production : For 3-node
            DeploymentType="Production"	
            
            #leave it if you want default IPvAddressType to be taken which is static
            # else change it to "Dynamic"
            IPv4AddressType=""
                        
            #Host Group to be Managed by Network Controller
            NCHostGroupName="NCManaged"
            
            #########################################################################
            #  Section to be filled if the Logical switch and Logical Network       #
            #  is already deployed for NC. You should do this if SET ( teamed)      #
            #  support is required. If you want VMM express to deploy the Logical   #
            #  switch and Management Network, skip this section.                    # 
            #  NOTE : This script assumes either you have both logical switch and   #
            # logical Network created and deployed or else you will use the script  #
            # to deploy both.                                                       #
            #########################################################################
            
            #Do you have an existing logical switch and the switch is deployed on all
            #the host you wish to Manage by NC
            IsLogicalSwitchDeployed = $true
            
            #if above is true give the name of logical switch			
            LogicalSwitch  = "Example_LS"

            # Do you have existing Management Network that you would like to use
            IsManagementVMNetworkExisting = $true

            #if above is true give the name of ManagementVMNetwork
            ManagementVMNetwork = "Example_LN" 

            #Uplink Port Profile to be used
            UplinkPortProfile = "Example_UPP"            
             
            #====================================================================================
            # The below set of Parameters are required for creation of Management Logical Network
            # and other Logical Networks Managed by NC.                                           
            # NOTE : If you already have Management Logical Network Created and switch deployed,
            # you don't need to specify any paramenet for "NC_Management" LN
            #====================================================================================
             LogicalNetworks = @(
			    @{
                    Name = "HNVPA"
                    Subnets = @(
                        @{
                            VLANID = 2017                                            #Example: 11
                            AddressPrefix = "10.10.10.0/23"                          #Example: "10.0.10.0/24"
                            DNS = @("10.177.16.161", "10.184.77.31", "10.184.77.32") #Example: @("10.0.0.7", "10.0.0.8", "10.0.0.9")
                            Gateways = "10.10.56.1"                                  #Example: "10.0.10.1"
                            PoolStart = "10.10.56.10"                                #Example: "10.0.10.50"
                            PoolEnd = "10.10.56.29"                                  #Example: "10.0.10.150"
                       }
                    )
                },
				@{
                    Name = "Transit"
                    Subnets = @(
                        @{
                            VLANID = 562                                             #Example: 12
                            AddressPrefix = "10.20.110.0/23"                        #Example: "10.0.40.0/24"
                            DNS = @("10.177.16.161", "10.184.77.31")                 #Example: @("10.0.0.7", "10.0.0.8", "10.0.0.9")
                            Gateways = "10.20.110.1"                                #Example: "10.0.40.1"
                            PoolStart = "10.20.111.10"                              #Example: "10.0.40.5"
                            PoolEnd = "10.20.111.29"                                #Example: "10.0.40.100"
                        }  
                    )
                }, 
                @{
                    #The first IP address (PoolStart) for this logical network is 
                    #automatically assigned to the SLB Manager.Other addresses such
                    #as the GatewayPublicIPAddress will start after that.
                    Name = "PublicVIP"
                    Subnets = @(
                        @{
                            VLANID = 0
                            AddressPrefix = "10.20.120.0/23"                        #Example: "10.0.20.0/24"
                            DNS = @("10.10.20.151")                                  #Example: @("10.0.0.7", "10.0.0.8", "10.0.0.9")
                            Gateways = "10.184.120.10"                               #Example: "10.0.20.1"
                            PoolStart = "10.184.121.10"                              #Example: "10.0.20.5"
                            PoolEnd = "10.184.121.30"                                #Example: "10.0.20.100"
                            IsPublic = $true
                        }  
                    )
                },
                @{
                    #The first IP address (PoolStart) for this logical network is 
                    #automatically assigned to the SLB Manager.Other addresses such
                    #as the GatewayPublicIPAddress will start after that.
                    Name = "PrivateVIP"
                    Subnets = @(
                        @{
                            VLANID = 0
                            AddressPrefix = "10.40.150.0/23"                         #Example: "10.0.20.0/24"
                            DNS = @("10.10.21.151")                                   #Example: @("10.0.0.7", "10.0.0.8", "10.0.0.9")
                            Gateways = "10.40.150.10"                                #Example: "10.0.20.1"
                            PoolStart = "10.40.151.10"                               #Example: "10.0.20.5"
                            PoolEnd = "10.40.151.30"                                 #Example: "10.0.20.100"
                            IsPublic = $false
                        }  
                    )
                },				
                @{
				    #if Management VM Network is not deployed give the ManagementVMNetwork information. Skip
                    # this if you already have this created.
                    Name = "NC_Management"
                    Subnets = @(
                    @{
                        VLANID = 561                                                  #Example: 7
                        AddressPrefix = "10.50.108.0/23"                             #Example: "10.0.0.0/24"
                        DNS = @("10.177.16.161", "10.184.77.31")                      #Example: @("10.0.0.7", "10.0.0.8", "10.0.0.9")
                        Gateways = "10.50.108.1"                                     #Example: "10.0.40.1"
                        PoolStart = "10.50.109.51"                                   #Example: "10.0.0.5"
                        PoolEnd = "10.50.109.69"                                     #Example: "10.0.0.100"
                        ReservedIPset =  "10.50.109.61"                              #This IP will be used for NC Rest API                           
                    }  
                    )
                }

            )
								
            #=========================================================================================
            # The following set of paremeters are required for importing VMM service Template,
            # configuring the Service Template and Deploying the service Template.
            #==========================================================================================

            # Make this true if self signed certificate is to be used
            # Example : $True , $False
            IsCertSelfSigned = $true  

            #The password for server certificate. This sertificate will be installed on the Host
            ServerCertificatePassword="!!Password"	            

            # The following are service settings required for configuring and
            # deploying the service template imported client security Group Name
            ClientSecurityGroupName= "Example\Domain Users"

            # Local Admin credentials
            # The local admin user name will be .\Administrator
            LocalAdminPassword= "Password123!!"    

            # Management Domain Account Which will be used for NC Deployment
            ManagementDomainUser="ExampleLab\ExampleUser"
            ManagementDomainUserPassword="Password!!"

             # This is the domain which NC VMs will join
            ManagementDomainFDQN="Example.com"		

            #Managemet Security Group Name
            ManagementSecurityGroupName="ExampleLab\Example NC Management"


            
            # Prefix to be added to infrastructural VMs created. Put the prefix such
            # that it makes VM name unique as this is the machine name of VM and should be unique.            
            ComputerNamePrefix = "NCE"   

            RestName = "VMMExpress"
            
            ##################################
            #  Deoloyment Control Switches   #
            ##################################
                                    
            # Do you want to deploy NC
            DeployNC = $true
            
            #Do you want to Deploy SLB
            DeploySLB = $true  

            #Do you want to deploy GW. 
            DeployGW = $true  			
        };
           
     );
}

VMMExpress.ps1

# --------------------------------------------------------------
#  Copyright © Microsoft Corporation.  All Rights Reserved.
#  Microsoft Corporation (or based on where you live, one of its affiliates) licenses this sample code for your internal testing purposes only.
#  Microsoft provides the following sample code AS IS without warranty of any kind. The sample code arenot supported under any Microsoft standard support program or services.
#  Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose.
#  The entire risk arising out of the use or performance of the sample code remains with you.
#  In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the code be liable for any damages whatsoever
#  (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss)
#  arising out of the use of or inability to use the sample code, even if Microsoft has been advised of the possibility of such damages.
# ---------------------------------------------------------------
<#
.SYNOPSIS 
    Deploys and configures the Microsoft SDN infrastructure from VMM, 
    including creation of the network controller VMs.  Then the VMs and Hyper-V hosts are configured to be 
    used by the Network Controller.  When this script completes the SDN 
    infrastructure is ready to be fully used for workload deployments with some exception to configure
    SLB and gateway depending upon your Data Center needs
.EXAMPLE
    .\VMMExpress -ConfigurationDataFile .\MyConfig.psd1
    Reads in the configuration from a PSD1 file that contains a hash table 
    of settings data.
.EXAMPLE
    .\VMMExpress -ConfigurationData $MyConfigurationData
    Uses the hash table that is passed in as the configuration data.  This 
    parameter set is useful when programatically generating the 
    configuration data. The generated config file should be similar to 
    fabricconfig.psd1
.NOTES
    Prerequisites:
    * All Hyper-V hosts must have Hyper-V enabled 
    * All Hyper-V hosts must be joined to Active Directory.
    * All Hyper-V host must be part of a single host group
    * VMM Library should have the VHD or VHDX used for creating infrastructural VMs.
    * The physical network must be preconfigured for the necessary subnets and 
    VLANs as defined in the configuration data.
    
#>

[CmdletBinding(DefaultParameterSetName="NoParameters")]
param(
    [Parameter(Mandatory=$true,ParameterSetName="ConfigurationFile")]
    [String] $ConfigurationDataFile=$null,
    [Parameter(Mandatory=$true,ParameterSetName="ConfigurationData")]
    [object] $ConfigurationData=$null
)


$Logfile  = split-path $pwd
$Logfile  = $Logfile + "\log\VMMExpress.log"

Function LogWrite
{
   Param ([string]$logstring)

   Add-content $Logfile -value $logstring
}

# Function to check and verify that all the required parameters are specified
# in case of missing parameter it throws exception and the execution stops.
function checkParameters
{
	param([Object] $ConfigData)
	
	
	# check that the input VHD is present in VMM library
	if ($ConfigData.VHDName -eq "") 
	{
		write-host "Error :VHD Name can not be blank . This is required for creating NC infrastructure VMs" -foregroundcolor "Red"
		exit -1
	}
	elseif( $ConfigData.VHDName.length -gt 64)
	{
		write-host "Error :Cannot validate argument on parameter 'VHDName'. The character length of the $ConfigData.VHDName.length argument is too long. Shorten the character length of the argument so it is fewer than or equal to 64 characters" -foregroundcolor "Red"
		exit -1
	}
	else
	{
	    $Vhd = Get-SCVirtualHardDisk -Name $ConfigData.VHDName
		if($Vhd.count -eq 0)
		{
			write-host "Error : Specified VHD does not exist in VMM library." -foregroundcolor "Red"
			exit -1
		}
		elseif($Vhd.count -gt 1)
		{
			write-host "Error : More than 1 VHD exists with this name" -foregroundcolor "Red"
			exit -1
		}
	}	
	# check the product Key
	if($ConfigData.ProductKey -eq "")
	{
		write-Host " WARNING: The product Key is blank. Specify the Product key by logging into the infrastructure VM while is it being configured" -foregroundcolor "Yellow"
	}
	#check the generation
	if(($ConfigData.Generation -eq "") -or (($ConfigData.Generation -ne "Gen1") -and ($ConfigData.Generation -ne "Gen2")))
	{
	    write-Host " Error: Generation must have a value Gen1 or Gen2" -foregroundcolor "Red"
		exit -1
	}
	#Check deployment type parameter
	if($ConfigData.DeploymentType -ne "Standalone" -and $ConfigData.DeploymentType -ne "Production")
	{
		write-Host " Error: Deployment Type must have a value Standalone or Production" -foregroundcolor "Red"
		exit -1
	}
	#Check the Host group 
	if($ConfigData.NCHostGroupName -eq "")
	{
		write-Host " Error: NCHostGroup Can not be blank" -foregroundcolor "Red"
		exit -1
	}
	else
	{
		$hostGroup = Get-SCVMHostGroup -Name $ConfigData.NCHostGroupName
		if($hostGroup.count -eq 0)
		{
			write-Host " Error: The specified NCHostGroup does not exist " -foregroundcolor "Red"
			exit -1
		}
		
	}
	
	#Check existing ManagementVMNetwork and Logical Switch deployment 
	
	if($ConfigData.IsManagementVMNetworkExisting -eq $true)
	{
	    
	    if($ConfigData.ManagementVMNetwork -eq "")
		{
			write-Host "Error: Existing VM Network Name can not be blank if IsManagementVMNetworkExisting = true " -foregroundcolor "Red"
			exit -1
		}
		write-host " VMNetwork Name : [$ConfigData.ManagementVMNetwork] "
		try{
	    $existingVMNetwork = Get-SCVMNetwork -Name $ConfigData.ManagementVMNetwork
		
		}
		catch
		{
		  write-host "Error getting Management network"
		}
		
		if($existingVMNetwork.count -eq 0 -or $existingVMNetwork.count -gt 1)
		{
			write-Host " Error: Existing VM Network either does not exist or there are multiple VMNetwork with same name " -foregroundcolor "Red"
			exit -1
		}
     
	}
	
	if($ConfigData.IsLogicalSwitchDeployed -eq $true)
	{
	   	if($ConfigData.LogicalSwitch -eq "")
		{
		  write-Host " Error: Existing Logical switch name can not be blank if IsLogicalSwitchDeployed = true  " -foregroundcolor "Red"
		  exit -1
		}
		$logicalswitch = Get-SCLogicalSwitch -Name $ConfigData.LogicalSwitch
		
		if($logicalswitch.count -eq 0 -or $logicalswitch.count -gt 1)
		{
			write-Host " Error: Existing Logical switch either does not exist or there are multiple LogicalSwitch with same name " -foregroundcolor "Red"
			exit -1
		}
		else
		{
		     # The Logical Switch should either be deployed on all the host in host group or non
			$Hosts = @(Get-SCVMHost | where {$_.VMHostGroup -eq $ConfigData.NCHostGroupName})
            
			foreach($VMHost in $Hosts){
                #get the virtual switch on this host. The virtual switch name should be same as logical switch
				$virtualNetwork = Get-SCVirtualNetwork -VMHost $VMHost | where {$_.LogicalSwitch.Name -eq $ConfigData.LogicalSwitch } 
				if($virtualNetwork.count -eq 0)
				{
				    write-Host " Error: Logical Switch is not deployed on Host : [$VMHost.Name] " -foregroundcolor "Red"
			        exit -1
				}	
            }
		}		
	}		
}

function OnBoardNetworkController
{
    param([Object] $node,
	[Object] $ManagementSubnet,
        [string] $VMName)
 
    LogWrite "VMName while onboarding NC : [$VMName]"
    $VMName = $VMName.Trim()
	$runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA"
	$configurationProvider = Get-SCConfigurationProvider -Name "Microsoft Network Controller"
	$vmHostGroup = @()
	$vmHostGroup += Get-SCVMHostGroup -Name $node.NCHostGroupName
	$certificates = @()
	if ($node.DeploymentType -eq "Production")
	{
	 
		$certificates += Get-SCCertificate -ComputerName $node.RestName -TCPPort 443
	}
	else
	{
		 $certificates += Get-SCCertificate -ComputerName $VMName -TCPPort 443
	}
	$ConnectionString = "serverurl=https://"
	if ($node.DeploymentType -eq "Production")
	{
		 $ConnectionString += $node.RestName
	}
	else
	{
		 $ConnectionString += $VMName		
	     $ConnectionString += "/;SouthBoundIPAddress="
		 $vm = Get-SCVirtualMachine -Name $VMName
		 $IPv4Address = $vm.VirtualNetworkAdapters[0].IPv4Addresses
		 $ConnectionString += $IPv4Address
	}
	$ConnectionString += ";servicename=NC"
	Write-Host "COnnection String :" $ConnectionString
	$NC = Add-SCNetworkService -Name "Network Controller" -RunAsAccount $runAsAccount -ConfigurationProvider $configurationProvider -VMHostGroup $vmHostGroup -ConnectionString $ConnectionString -Certificate $certificates -ProvisionSelfSignedCertificatesForNetworkService $node.IsCertSelfSigned
}

function importServiceTemplate
{
    param([Object] $node) 
	#identify the name of service template
	$serviceTemplateLocation = Split-Path -Path $pwd
			$serviceTemplateLocation = $serviceTemplateLocation + "\Templates\NC\"
	$ServiceTemplateName = "Network Controller "
	if($node.DeploymentType -eq "Standalone")
	{
		$ServiceTemplateName = $ServiceTemplateName + "Standalone "
	}
	if($node.DeploymentType -eq "Production")
	{
		$ServiceTemplateName = $ServiceTemplateName + "Production "
	}   
	
	if($node.Generation -eq "Gen1")
	{
		$ServiceTemplateName = $ServiceTemplateName + "Generation 1 VM.xml"
	}
	if($node.Generation -eq "Gen2")
	{
		$ServiceTemplateName = $ServiceTemplateName + "Generation 2 VM.xml"
	}    
	$PackagePath = $serviceTemplateLocation + $ServiceTemplateName
	
	#Get the package
	$package = Get-SCTemplatePackage -Path $PackagePath
	
	#Get the package mapping
	$allMappings = New-SCPackageMapping -TemplatePackage $package
	
	#start mapping the resources
	
	#MAP the VHD
	LogWrite "Mapping VHD to template package"
	if($node.Generation -eq "Gen1")
	{
	$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhd"}
	}
	else
	{
	$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"}
	}
	$resource = Get-SCVirtualHardDisk -Name $node.VHDName
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource
	
	#MAP NCsetup.cr
	#$VMMLibrary = $node.VMMLibrary
	$VMMLibrary = Get-SCLibraryShare
	$NCsetupPath = $serviceTemplateLocation + "NCSetup.cr\"
	Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath -SharePath $VMMLibrary[0] -OverwriteExistingFiles
	
	LogWrite "Mapping NCSetup.cr to template package"
	$mapping = $allMappings | where {$_.PackageId -eq "NCSetup.cr"}
	$resource = Get-SCCustomResource -Name "NCSetup.cr"
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource 
	
	#MAP ServerCertificate.cr
	$NCsetupPath = $serviceTemplateLocation + "ServerCertificate.cr\"
	Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath -SharePath $VMMLibrary[0] -OverwriteExistingFiles
	
	LogWrite "Mapping NCSetup.cr to template package"
	$mapping = $allMappings | where {$_.PackageId -eq "ServerCertificate.cr"}
	$resource = Get-SCCustomResource -Name "ServerCertificate.cr"
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource
	
	#MAP TrustedRootCertificate.cr
	$NCsetupPath = $serviceTemplateLocation + "TrustedRootCertificate.cr\"
	Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath -SharePath $VMMLibrary[0] -OverwriteExistingFiles
	
	LogWrite "Mapping NCSetup.cr to template package"
	$mapping = $allMappings | where {$_.PackageId -eq "TrustedRootCertificate.cr"}
	$resource = Get-SCCustomResource -Name "TrustedRootCertificate.cr"
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource
	
	#Import the service TemplatePackage
	$serviceTemplate = Import-SCTemplate -TemplatePackage $package -Name "NC Deployment service Template" -PackageMapping $allMappings -Release "1.0" -SettingsIncludePrivate

        #update the computer Name 
        if($node.IPv4AddressType -ne "")
        {
            $VirtualNetworkAdapter = Get-SCVirtualNetworkAdapter -ALL | where {$_.Name -eq "Windows Server Network Controller"}
            Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VirtualNetworkAdapter -IPv4AddressType $node.IPv4AddressType
        }

        $Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "NC-VM##"}
        $ComputerNamePattern = $node.ComputerNamePrefix + "-NCVM##"
        Set-SCVMTemplate -Template $Template -ComputerName $ComputerNamePattern -ProductKey $node.ProductKey
}

function GetVMName
{
    # Get the VM Name which VMM will be applying for NC VM
	param([object] $node)
	
	$VMName = ""
	$VMNumericValue = 1
	
	for($VMNumericValue = 1; $VMNumericValue -le 99 ; $VMNumericValue++)
	{
	    $VMName = $node.ComputerNamePrefix + "-NCVM"
		if($VMNumericValue -lt 10)
		{
			$VMName = $VMName + "0"
			$VMName = $VMName + $VMNumericValue
		}
		else
		{
			$VMName = $VMName + $VMNumericValue
		}
		$VMName = $VMName +"."
                $VMName = $VMName + $node.ManagementDomainFDQN
		$VM = Get-SCVirtualMachine -Name $VMName
		if ($VM.count -eq 0){break}
	}
        
        LogWrite "Trimmed VMName:[$VMName]"
        return [string]$VMName

}

function generateSelfSignedCertificate
{
    param([object] $node)
		
	LogWrite " Generating the self signed certificate "
	$certFriendlyName = "NC certificate"
	
	# Get the VM Name which VMM will be applying for NC VM
	$VMName1 = GetVMName $node
	$VMName = $VMName1.ToString()
	$VMName = $VMName.Trim()
	if($node.DeploymentType -eq "Standalone")
	{
		$dnsName = $VMName
	}   
	if ($node.DeploymentType -eq "Production")
	{
		#$dnsName = $ManagementSubnet.ReservedIPset
		$dnsName = $node.RestName
	}
	#else
	#    LogWrite "Certificate can not be generated"
	
	$generatedCert = New-SelfSignedCertificate -KeyUsageProperty All -Provider "Microsoft Strong cryptographic provider" -FriendlyName $certFriendlyName -DnsName $dnsName
	
	#Export the pfx cert file
	LogWrite "Exporting the certificate"
	$certPassword = ConvertTO-SecureString -String $node.ServerCertificatePassword -Force -AsPlainText
	$certPath = "cert:\LocalMachine\My\" + $generatedCert.Thumbprint

	Write-Host " Certificate Path : $certPath" 
	
	#The File path parameter should be path of downloaded service template servercertificate.cr folder for NC
	$Exportedcert = Export-pfxCertificate  -Cert $certPath  -FilePath "..\Templates\NC\ServerCertificate.cr\ServerCert.pfx" -Password $certPassword
	
	#Export the cert for SLB
	$Exportedcert = Export-Certificate -Cert $certPath  -FilePath "..\Templates\SLB\NCCertificate.cr\MCCert.cer"
        
}

function configureAndDeployService
{
    param([object]$node,
          [object]$ManagementVMNetwork,
          [object]$ManagementSubnet
		  )
		
	LogWrite "Starting Service Template Configuration"
	
	# Get the host group on which the service is to be deployed
	$ServiceHostGroup = Get-SCVMHostGroup -Name $node.NCHostGroupName
	
	#Get the service template
	$serviceTemplate = Get-SCServiceTemplate -Name "NC Deployment service Template"
	
	#Create a new service configuration
	$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate -Name "NC" -VMHostGroup $ServiceHostGroup

	#update the Management Network in Service Config
        LogWrite " getting Management Network [$LogicalNetworkCreated]"
	#$ManagementNetwork = Get-SCVMNetwork -Name "NC_Management"
	if($node.ManagementVMNetwork -eq "")
	{
	$ManagementVMNetwork = Get-SCVMNetwork -Name "NC_Management"
	}
	else
	{
		$ManagementVMNetwork = Get-SCVMNetwork -Name $node.ManagementVMNetwork
	}
	
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "Management" |Set-SCServiceSetting  -value $ManagementVMNetwork.ID

	#update the service configuration to apply placement. If there is any error,Lets stop
	$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig
	if($ServiceUpdate.deploymenterrorlist -ne $null)
	{       

		Write-Host "Placement failed for Service Deployment"
		exit -1
	}
	
	#set the service template settings
  
        LogWrite "Getting the service setting"
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "ClientSecurityGroup" |Set-SCServiceSetting  -value $node.ClientSecurityGroupName
  
	# Create the Local Admin Run As Account
        LogWrite "Creating Account"
	$localAdminCredPassword = ConvertTo-SecureString -String $node.LocalAdminPassword -Force -AsPlainText
	$localAdminCred = New-Object System.Management.Automation.PSCredential (".\Administrator", $localAdminCredPassword)
	$localAdminRAA = New-SCRunAsAccount -Name "NC_LocalAdminRAA" -Credential $localAdminCred -NoValidation
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "LocalAdmin" |Set-SCServiceSetting  -value $localAdminRAA                        
  
	# Create NC domain admin Run As Account
	$MgmtDomainCredPassword = ConvertTo-SecureString -String $node.ManagementDomainUserPassword -Force -AsPlainText
	$MgmtDomainCred = New-Object System.Management.Automation.PSCredential ($node.ManagementDomainUser, $MgmtDomainCredPassword)
	$MgmtAdminRAA = New-SCRunAsAccount -Name "NC_MgmtAdminRAA" -Credential $MgmtDomainCred
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtDomainAccount" |Set-SCServiceSetting  -value $MgmtAdminRAA             
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtDomainAccountName" |Set-SCServiceSetting  -value $node.ManagementDomainUser
	$domainpwd = ConvertTo-SecureString -String $node.ManagementDomainUserPassword -Force -AsPlainText
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtDomainAccountPassword" |Set-SCServiceSetting  -Securevalue $domainpwd
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtDomainFQDN" |Set-SCServiceSetting  -value $node.ManagementDomainFDQN
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtSecurityGroup" |Set-SCServiceSetting  -value $node.ManagementSecurityGroupName
	$certpassword = ConvertTo-SecureString -string $node.ServerCertificatePassword -Force -AsPlainText 
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "ServerCertificatePassword" |Set-SCServiceSetting  -Securevalue $certpassword
	
	if ($node.DeploymentType -eq "Production")
	{
	    Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "RestEndPoint" |Set-SCServiceSetting  -value $node.RestName
	}
	
	#create Instance of the service
    try{
    
        $sc= New-SCService -ServiceConfiguration $ServiceConfig
    }
    catch
    {
        undoNCDeployment $node
    }
}

function undoNCDeployment
{
	param([Object] $node)
	
    if ($NetworkControllerOnBoarder -eq $false)
    {
        # Remove the network service
        $NS = Get-SCNetworkService -All | where {$_.Name -eq "Network Controller"}
        if($NS.count -gt 0)
        {
            Remove-SCNetworkService -NetworkService $NS
        }
        
        #Remove the NC service instance
        $SCService = get-SCService -Name "NC"
        if($SCService.count -gt 0)
        {
            Remove-SCService -Service $SCService
        }
        
        #Remove service Template
        $ServiceTemplate = Get-SCServiceTemplate -Name "NC Deployment service Template"
        if($ServiceTemplate.count -gt 0)
        {
            Remove-SCServiceTemplate -ServiceTemplate $ServiceTemplate
        }
        
        #Remove Run AS Accounts
        $RA = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA"
        if($RA.count -gt 0)
        {
            Remove-SCRunAsAccount -RunAsAccount $RA
        }
        
        $RA = Get-SCRunAsAccount -Name "NC_LocalAdminRAA"
        if($RA.count -gt 0)
        {
            Remove-SCRunAsAccount -RunAsAccount $RA
        }
        
        #Remove Virtual switches from all the Hosts
        if($node.IsLogicalSwitchDeployed -eq $false)
        {
            $Hosts = @(Get-SCVMHost | where {$_.VMHostGroup -eq $node.NCHostGroupName})
            
            if($Hosts.count > 0)
            {	
                foreach($VMHost in $Hosts){
                    $virtualSwitch = Get-SCVirtualNetwork -Name "NC_LogicalSwitch" -VMHost $VMHost
                    if($virtualSwitch.count -gt 0)
                    {
                        Remove-SCVirtualNetwork -VirtualNetwork $virtualSwitch
                        Set-SCVMHost -VMHost $VMHost
                    }
                }
            }
            
            #Remove Management Network IP Pool
            $Ippool = Get-SCStaticIPAddressPool -Name "NC_Management_IPAddressPool_0"
            if($Ippool.count -gt 0)
            {
                Remove-SCStaticIPAddressPool -StaticIPAddressPool $Ippool
            }
                
            #Remove Logical Switch
            $LS = Get-SCLogicalSwitch -Name "NC_LogicalSwitch"
            if($LS.count -gt 0)
            {
                Remove-SCLogicalSwitch -LogicalSwitch $LS 
            }
            
            #Remove uplink
            $Uplink = Get-SCNativeUplinkPortProfile -Name $node.UplinkPortProfile
            if(Uplink.count -gt 0)
            {
                Remove-SCNativeUplinkPortProfile -NativeUplinkPortProfile $Uplink
            }
            

            
            #Remove Management VM Network
            $VMNetwork = Get-SCVMNetwork -Name "NC_Management"
            Remove-SCVMNetwork -VMNetwork $VMNetwork
            
            #Remove Logical Network
                #Remove Network Definition
            $logicalNetwork = Get-SCLogicalNetwork -Name "NC_Management"
            if($logicalNetwork.count -gt 0)
            {
                Set-SCLogicalNetwork -Name "NC_Management" -Description "" -LogicalNetwork $logicalNetwork -RunAsynchronously -EnableNetworkVirtualization $false -UseGRE $false -LogicalNetworkDefinitionIsolation $false
                $logicalNetworkDefinition = Get-SCLogicalNetworkDefinition -Name "NC_Management_0"
                if($logicalNetworkDefinition.count -gt 0)
                {
                Remove-SCLogicalNetworkDefinition -LogicalNetworkDefinition $logicalNetworkDefinition
                }        
                Remove-SCLogicalNetwork -LogicalNetwork $logicalNetwork	
            }
        }
    }
}

function createLogicalNetwork
{
    param([Object] $node,
          [object] $ln,
          [boolean] $ManagedByNC
          )
    
    
	if($ManagedByNC -eq $true)
	{
		$NetController = Get-SCVirtualSwitchExtensionManager -All | where{$_.Name -eq "Network Controller"}
		if($ln.Name -eq "PublicVIP")
		{
			
		    $LogicalNetworkCreated = New-SCLogicalNetwork -Name $ln.Name -LogicalNetworkDefinitionIsolation $false -EnableNetworkVirtualization $false -UseGRE $false -IsPVLAN $false -NetworkController $NetController -PublicIPNetwork
		}
		elseif($ln.Name -eq "PrivateVIP" -or $ln.Name -eq "GREVIP")	
		{
			$LogicalNetworkCreated = New-SCLogicalNetwork -Name $ln.Name -LogicalNetworkDefinitionIsolation $false -EnableNetworkVirtualization $false -UseGRE $false -IsPVLAN $false -NetworkController $NetController
		}
	    elseif($ln.Name -eq "HNVPA")
		{
			$LogicalNetworkCreated = New-SCLogicalNetwork -Name $ln.Name -LogicalNetworkDefinitionIsolation $false -EnableNetworkVirtualization $true -UseGRE $true -IsPVLAN $false -NetworkController $NetController 
	
		}
		else
		{
			$LogicalNetworkCreated = New-SCLogicalNetwork -Name $ln.Name -LogicalNetworkDefinitionIsolation $false -EnableNetworkVirtualization $false -UseGRE $false -IsPVLAN $false -NetworkController $NetController 
		}
		
	}
	else
	{
		$LogicalNetworkCreated = New-SCLogicalNetwork -Name $ln.Name -LogicalNetworkDefinitionIsolation $false -EnableNetworkVirtualization $false -UseGRE $false -IsPVLAN $false
	}			
    LogWrite "Getting the Host group with Name [$node.NCHostGroupName]"
    $allHostGroups = @()
    $allHostGroups += Get-SCVMHostGroup -Name $node.NCHostGroupName

    LogWrite "Creating VLAN subnet for subnet [$node.LogicalNetworkIPSubnet] and VLAN Id [$node.LogicalNetworkVLAN]"
    $allSubnetVLAN = @()
    $allSubnetVLAN += New-SCSubnetVLAN -Subnet $ln.subnets[0].AddressPrefix -VLanID $ln.subnets[0].VLANID
    $VLANId = $ln.subnets[0].VLANID

    LogWrite "Creating new Logical Network Definition"
    $LNDName = $ln.Name + "_0"    
    $createdLND = New-SCLogicalNetworkDefinition -Name $LNDName -LogicalNetwork $LogicalNetworkCreated -VMHostGroup $allHostGroups -SubnetVLan $allSubnetVLAN

    LogWrite " create a VMNetwork with the same name as Logical Network"
    if($ln.Name -ne "HNVPA")
    {
    $ManagementVMNetwork = New-SCVMNetwork -Name $ln.Name -IsolationType "NoIsolation" -LogicalNetwork $LogicalNetworkCreated
    }

    LogWrite "Management Logical Network Deployment completed succssfully"

    #Create IP Pool for the created Management Logical Network
    $subnet = $ln.subnets[0]
    $ManagementSubnet = $subnet
    $allGateways =@()
    $allGateways += New-SCDefaultGateway -IPAddress $subnet.Gateways -Automatic

    if($subnet.DNS.count -eq 0)
    {
        LogWrite " DNS setttings are mandatory for NC deployment to succeed"
        return -1
    }
                    
    $IPAddressPoolName = $ln.Name + "_IPAddressPool_0"

    if($ln.Name -eq "NC_Management")
    {
        $staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName -LogicalNetworkDefinition $createdLND -Subnet $subnet.AddressPrefix -IPAddressRangeStart $subnet.PoolStart -IPAddressRangeEnd $subnet.PoolEnd -DefaultGateway $allGateways -DNSServer $subnet.DNS -IPAddressReservedSet $subnet.ReservedIPset
    }
    elseif($ln.Name -eq "PublicVIP" -or $ln.Name -eq "PrivateVIP" -or $ln.Name -eq "GREVIP" )
    {
       $VIPAddressSet = ""
       $VIPAddressSet += $subnet.PoolStart 
       $VIPAddressSet += "-" 
       $VIPAddressSet += $subnet.PoolEnd
	   $staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName -LogicalNetworkDefinition $createdLND -Subnet $subnet.AddressPrefix -IPAddressRangeStart $subnet.PoolStart -IPAddressRangeEnd $subnet.PoolEnd -DefaultGateway $allGateways -DNSServer $subnet.DNS -VIPAddressSet $VIPAddressSet
	}
    else
    {
        $staticIP = New-SCStaticIPAddressPool -Name $IPAddressPoolName -LogicalNetworkDefinition $createdLND -Subnet $subnet.AddressPrefix -IPAddressRangeStart $subnet.PoolStart -IPAddressRangeEnd $subnet.PoolEnd -DefaultGateway $allGateways -DNSServer $subnet.DNS
    }


    LogWrite " Created Logical Network : $LogicalNetworkCreated"
    return $LogicalNetworkCreated
}

function createLogicalSwitchAndDeployOnHosts
{
    param ([object] $node,
           [object] $ManagementVMNetwork,
           [string] $LNDName,
           [Int] $VLANId)

    $logicalSwitchName = "NC_LogicalSwitch"
    LogWrite " creating logical switch [$logicalSwitchName]"
    
    #TODO: Handle the teaming aspect as well. Need to get the required parameters from user for this.
    $createdLogicalSwitch = New-SCLogicalSwitch -Name $logicalSwitchName -Description "This logical switch is used for SDN purpose" -EnableSriov $false -SwitchUplinkMode "NoTeam" -MinimumBandwidthMode "Weight"
    
    #Add uplink profile and VNic to the switch
    $LogicalNetworkDefinition = @()
    $LogicalNetworkDefinition += Get-SCLogicalNetworkDefinition -Name $LNDName
    $createdUpLinkProfile = New-SCNativeUplinkPortProfile -Name $node.UplinkPortProfile -Description " This uplink is used by Logical Switch for SDN" -LogicalNetworkDefinition $LogicalNetworkDefinition -EnableNetworkVirtualization $false -LBFOLoadBalancingAlgorithm "HostDefault" -LBFOTeamMode "SwitchIndependent"
    
    # set the uplink port profile to Logical Switch
    $uppSetVar = New-SCUplinkPortProfileSet -Name $createdUpLinkProfile.Name -LogicalSwitch $createdLogicalSwitch -NativeUplinkPortProfile $createdUpLinkProfile
    
    #Add VNic to the UpLink Port Profile
    if($VLANId -eq 0)
    {
       $VNic = New-SCLogicalSwitchVirtualNetworkAdapter -Name "NC_VNic" -UplinkPortProfileSet $uppSetVar -VMNetwork $ManagementVMNetwork -VLanEnabled $false -IsUsedForHostManagement $true -InheritsAddressFromPhysicalNetworkAdapter $true -IPv4AddressType "Dynamic" -IPv6AddressType "Dynamic"
    }
    else
    {
        $VNic = New-SCLogicalSwitchVirtualNetworkAdapter -Name "NC_VNic" -UplinkPortProfileSet $uppSetVar -VMNetwork $ManagementVMNetwork -VLanEnabled $true -VLANId $VLANId  -IsUsedForHostManagement $true -InheritsAddressFromPhysicalNetworkAdapter $true -IPv4AddressType "Dynamic" -IPv6AddressType "Dynamic"
    }
    
    # Deploy the Logical Switch on all host in the host group
		
    $Hosts = @(Get-SCVMHost | where {$_.VMHostGroup -eq $node.NCHostGroupName})

    foreach($VMHost in $Hosts){
		
        #Get network Adapter on this hots with VLANMode = Trunk and ConnectionState = connecetd. The
        #switch will be deployed on this physical NetworkAdapter
        $NetworkAdapter = @(Get-SCVMHostNetworkAdapter -VMHost $VMHost | where {$_.VLanMode -eq "Trunk" -and $_.ConnectionState -eq "Connected" -and $_.LogicalNetworkMap.count -eq 0})
        if($NetworkAdapter.count -eq 0)
        {
             Write-Host "ERROR: There is no available Network Adapter for NC Virtual Switch" -foregroundcolor "Red"
             exit -1
        }

        #Set the Network Adapter
        Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $NetworkAdapter[0] -UplinkPortProfileSet $uppSetVar

        #create new virtual Network
        New-SCVirtualNetwork -VMHost $VMHost -VMHostNetworkAdapters $NetworkAdapter[0] -LogicalSwitch $createdLogicalSwitch -DeployVirtualNetworkAdapters

        #Set the VMHost
        Set-SCVMHost -VMHost $VMHost
    }
    
    return $createdLogicalSwitch
}

function CreateLogicalNetworkWrapper
{
    param([object]$node,
	      [string]$LogicalNetworkType,
		  [boolean] $ManagedByNC)
		  
    foreach ($ln in $node.LogicalNetworks)
	{
		if($ln.Name -eq $LogicalNetworkType){
	
			LogWrite "Starting to create Management Logical Network [$LogicalNetworkType]"
			
			#Create the logical Network
			$LogicalNetworkCreated = createLogicalNetwork $node $ln $ManagedByNC
		}
	}
	return $LogicalNetworkCreated
}

function AssociateLogicalNetWithUPP
{
	param([string] $LogicalNetwork,
           [String] $UplinkPortProfile)
	
	#Get the LogicalNetwork
	$LogNet = Get-SCLogicalNetwork -Name $LogicalNetwork

    # Get the logical Network Definition
    $LogicalNetworkDefinition = Get-SCLogicalNetworkDefinition -LogicalNetwork 	$LogNet
	
	#Get the NC uplink port profile
	$uplink = Get-SCNativeUplinkPortProfile -Name $UplinkPortProfile
	
	#Set the uplink port profile
    Set-SCNativeUplinkPortProfile -NativeUplinkPortProfile $uplink -AddLogicalNetworkDefinition $LogicalNetworkDefinition
}

function ImportSLBServiceTemplate
{
	param ([object] $node)
	
	#identify the name of service template
	$serviceTemplateLocation = Split-Path -Path $pwd
	$serviceTemplateLocation = $serviceTemplateLocation + "\Templates\SLB\"
	$ServiceTemplateName = "SLB Production "
	
	if($node.Generation -eq "Gen1")
	{
		$ServiceTemplateName = $ServiceTemplateName + "Generation 1 VM.xml"
	}
	if($node.Generation -eq "Gen2")
	{
		$ServiceTemplateName = $ServiceTemplateName + "Generation 2 VM.xml"
	}    
	$PackagePath = $serviceTemplateLocation + $ServiceTemplateName
	
	#Get the package
	$package = Get-SCTemplatePackage -Path $PackagePath
	
	#Get the package mapping
	$allMappings = New-SCPackageMapping -TemplatePackage $package
	
	#start mapping the resources
	
	#MAP the VHD
	LogWrite "Mapping VHD to template package"
	if($node.Generation -eq "Gen1")
	{
	$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhd"}
	}
	else
	{
	$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"}
	}
	$resource = Get-SCVirtualHardDisk -Name $node.VHDName
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource
	
	#MAP NCsetup.cr
	#$VMMLibrary = $node.VMMLibrary
	$VMMLibrary = Get-SCLibraryShare
	$NCsetupPath = $serviceTemplateLocation + "\NCCertificate.cr\"
	Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath -SharePath $VMMLibrary[0] -OverwriteExistingFiles
	
	LogWrite "Mapping NCSetup.cr to template package"
	$mapping = $allMappings | where {$_.PackageId -eq "NCCertificate.cr"}
	$resource = Get-SCCustomResource -Name "NCCertificate.cr"
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource 
	

	#MAP ServerCertificate.cr
	$NCsetupPath = $serviceTemplateLocation + "\EdgeDeployment.cr\"
	Import-SCLibraryPhysicalResource -SourcePath $NCsetupPath -SharePath $VMMLibrary[0] -OverwriteExistingFiles
	
	LogWrite "Mapping NCSetup.cr to template package"
	$mapping = $allMappings | where {$_.PackageId -eq "EdgeDeployment.cr"}
	$resource = Get-SCCustomResource -Name "EdgeDeployment.cr"
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource
	
	#Import the service TemplatePackage
	$serviceTemplate = Import-SCTemplate -TemplatePackage $package -Name "SLB Deployment service Template" -PackageMapping $allMappings -Release "1.0" -SettingsIncludePrivate
	
    $Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "muxvm###"}
    $ComputerNamePattern = $node.ComputerNamePrefix + "-muxvm##"
    Set-SCVMTemplate -Template $Template -ComputerName $ComputerNamePattern -ProductKey $node.ProductKey
}

function ConfigureAndDeploySLBService
{

    param([object] $node)
    
    LogWrite "Starting Service Template Configuration for SLB"
	
	# Get the host group on which the service is to be deployed
	$ServiceHostGroup = Get-SCVMHostGroup -Name $node.NCHostGroupName
	
	#Get the service template
	$serviceTemplate = Get-SCServiceTemplate -Name "SLB Deployment service Template"

        #Resolve the service Template
        Resolve-SCServiceTemplate -ServiceTemplate $serviceTemplate -update

	
	#Create a new service configuration
	$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate -Name "Software Load Balancer" -VMHostGroup $ServiceHostGroup

	
	# Set Management Network
    if($node.IsManagementVMNetworkExisting -eq $true)
    {
        $ManagementNetwork = Get-SCVMNetwork -Name $node.ManagementVMNetwork
    }
    else
    {
    $ManagementNetwork = Get-SCVMNetwork -Name "NC_Management"
    }
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "ManagementNetwork" |Set-SCServiceSetting  -value $ManagementNetwork.ID
    
    # Set Transit Network
    $TransitNetwork = Get-SCVMNetwork -Name "Transit"
    Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "TransitNetwork" |Set-SCServiceSetting  -value $TransitNetwork.ID
    

	#update the service configuration to apply placement. If there is any error,Lets stop
	$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig
	if($ServiceUpdate.deploymenterrorlist -ne $null)
	{       

		Write-Host "Placement failed for Service Deployment"
		exit -1
	}
	
	#set the service template settings
      
	# Create the Local Admin Run As Account
    
	$localAdminRAA = Get-SCRunAsAccount -Name "NC_LocalAdminRAA" 
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "LocalAdmin" |Set-SCServiceSetting  -value $localAdminRAA                        
  
	# Create the Local Admin Run As Account
	$MgmtAdminRAA = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA" 
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtDomainAccount" |Set-SCServiceSetting  -value $MgmtAdminRAA             
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtDomainFQDN" |Set-SCServiceSetting  -value $node.ManagementDomainFDQN
    Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "SelfSignedConfiguration" |Set-SCServiceSetting  -value $node.IsCertSelfSigned
	
	#create Instance of the service
	$sc= New-SCService -ServiceConfiguration $ServiceConfig

}

function DeploySLB
{
	param([object] $node)
	
    #Import SLB Service Template
	ImportSLBServiceTemplate $node
	
	#Configure and Deploy SLB
	ConfigureAndDeploySLBService $node
}

function OnboardSLB
{
	param([object] $node)
	
	$networkService = Get-SCNetworkService -Name "Network Controller"
	
	$fabricRole = Get-SCFabricRole -NetworkService $networkService | where {$_.RoleType -eq "LoadBalancer"}
	
	#get the last IP address of Private VIP
	$ippool = Get-SCStaticIPAddressPool -Name "PrivateVIP_IPAddressPool_0"
	$LBManagerIPAddress = $ippool.IPAddressRangeEnd
	
    $vipPools = @()
    $vipPools += Get-SCStaticIPAddressPool -Name "PrivateVIP_IPAddressPool_0"
    $vipPools += Get-SCStaticIPAddressPool -Name "PublicVIP_IPAddressPool_0"
   	$natIPExemptions = @()
	
	$fabricRoleConfiguration = New-SCLoadBalancerRoleConfiguration -LBManagerIPAddress $LBManagerIPAddress -NatIPExemptions $natIPExemptions -VipPools $vipPools
	
    $fabricRole = Set-SCFabricRole -FabricRole $fabricRole -LoadBalancerConfiguration $fabricRoleConfiguration
	
	# Get Service Instance 'SLB'
    $service = Get-SCService -Name "Software Load Balancer"
    # Get RunAs Account 'NC_MgmtAdminRAA'
    $runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA"
    Add-SCFabricRoleResource -FabricRole $fabricRole -ServiceInstance $service -RunAsAccount $runAsAccount 
	
	
}

function importGatewayTemplate
{

	param ([object]$node)
	
	#identify the name of service template
	$serviceTemplateLocation = Split-Path -Path $pwd
			$serviceTemplateLocation = $serviceTemplateLocation + "\Templates\GW\"
	$ServiceTemplateName = "EdgeServiceTemplate_"
	
	if($node.Generation -eq "Gen1")
	{
		$ServiceTemplateName = $ServiceTemplateName + "Generation1.xml"
	}
	if($node.Generation -eq "Gen2")
	{
		$ServiceTemplateName = $ServiceTemplateName + "Generation2.xml"
	}    
	$PackagePath = $serviceTemplateLocation + $ServiceTemplateName
	
	#Get the package
	$package = Get-SCTemplatePackage -Path $PackagePath
	
	#Get the package mapping
	$allMappings = New-SCPackageMapping -TemplatePackage $package
	
	#start mapping the resources
	
	#MAP the VHD
	LogWrite "Mapping VHD to template package"
	if($node.Generation -eq "Gen1")
	{
	$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhd"}
	}
	else
	{
	$mapping = $allMappings | where {$_.PackageId -eq "Winserver.vhdx"}
	}
	$resource = Get-SCVirtualHardDisk -Name $node.VHDName
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource
	
	
	
	LogWrite "Mapping NCCertificate.cr to template package"
	$mapping = $allMappings | where {$_.PackageId -eq "NCCertificate.cr"}
	$resource = Get-SCCustomResource -Name "NCCertificate.cr"
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource 

	
	LogWrite "Mapping NEdgeDeployment.cr to template package"
	$mapping = $allMappings | where {$_.PackageId -eq "EdgeDeployment.cr"}
	$resource = Get-SCCustomResource -Name "EdgeDeployment.cr"
	Set-SCPackageMapping -PackageMapping $mapping -TargetObject $resource
	
	#Import the service TemplatePackage
	$serviceTemplate = Import-SCTemplate -TemplatePackage $package -Name "Gateway Deployment service Template" -PackageMapping $allMappings -Release "1.0" -SettingsIncludePrivate
	
	$Template = Get-SCVMTemplate -ALL | where {$_.ComputerName -eq "GW-VM###"}
    $ComputerNamePattern = $node.ComputerNamePrefix + "-GW-VM##"
    Set-SCVMTemplate -Template $Template -ComputerName $ComputerNamePattern -ProductKey $node.ProductKey

	
}

function ConfigureAndDeployGatewayService
{

    param([object] $node)
    
    LogWrite "Starting Service Template Configuration for Gateway"
	
	# Get the host group on which the service is to be deployed
	$ServiceHostGroup = Get-SCVMHostGroup -Name $node.NCHostGroupName
	
	#Get the service template
	$serviceTemplate = Get-SCServiceTemplate -Name "Gateway Deployment service Template"

    #Resolve the service Template
    Resolve-SCServiceTemplate -ServiceTemplate $serviceTemplate -update

	
	#Create a new service configuration
	$serviceConfig = New-ScServiceConfiguration -ServiceTemplate $serviceTemplate -Name "Gateway Manager" -VMHostGroup $ServiceHostGroup

	
	# Set Management Network
    if($node.IsManagementVMNetworkExisting -eq $true)
    {
        $ManagementNetwork = Get-SCVMNetwork -Name $node.ManagementVMNetwork
    }
    else
    {
        $ManagementNetwork = Get-SCVMNetwork -Name "NC_Management"
    }
    
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "ManagementNetwork" |Set-SCServiceSetting  -value $ManagementNetwork.ID
        

	#update the service configuration to apply placement. If there is any error,Lets stop
	$ServiceUpdate = Update-SCServiceConfiguration -ServiceConfiguration $ServiceConfig
	if($ServiceUpdate.deploymenterrorlist -ne $null)
	{       

		Write-Host "Placement failed for Service Deployment"
		exit -1
	}
	
	#set the service template settings
      
	# Create the Local Admin Run As Account
    
	$localAdminRAA = Get-SCRunAsAccount -Name "NC_LocalAdminRAA" 
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "AdminAccount" |Set-SCServiceSetting  -value $localAdminRAA                        
  
	# Create the Local Admin Run As Account
	$MgmtAdminRAA = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA" 
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtDomainAccount" |Set-SCServiceSetting  -value $MgmtAdminRAA             
	Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "MgmtDomainFQDN" |Set-SCServiceSetting  -value $node.ManagementDomainFDQN
    Get-SCServiceSetting -ServiceConfiguration $ServiceConfig -Name "SelfSignedConfiguration" |Set-SCServiceSetting  -value $node.IsCertSelfSigned
	
	#create Instance of the service
	$sc= New-SCService -ServiceConfiguration $ServiceConfig

}

function DeployGateway
{
	param([object]$node)
	
	#import Gateway template
	importGatewayTemplate $node
	
	#configure and deploy gateway
	ConfigureAndDeployGatewayService $node

}

function OnboardGateway
{
    param([object]$node)
    
    $networkService = Get-SCNetworkService -Name "Network Controller"
	
	$fabricRole = Get-SCFabricRole -NetworkService $networkService | where {$_.RoleType -eq "Gateway"}
	
	#get the last IP address of Private VIP
	$GREVIP = get-SCLogicalNetworkDefinition -Name "GREVIP_0"
	$subnetVlansGreVip = @()
    $subnetVlanGreVipIPv4 = New-SCSubnetVLan -Subnet $GREVIP[0].SubnetVLans[0].Subnet  -VLanID $GREVIP[0].SubnetVLans[0].VLanID
    $subnetVlansGreVip += $subnetVlanGreVipIPv4
    
    $ippool = Get-SCStaticIPAddressPool -Name "PublicVIP_IPAddressPool_0"
    $publicIPV4Address = Grant-SCIPAddress -PublicIPAddress -NetworkController $networkController -IPAddress $ippool.IPAddressRangeEnd
    $publicIPAddresses = @()
    $publicIPAddresses += $publicIPV4Address
	
	$fabricRoleConfiguration = New-SCGatewayRoleConfiguration -GatewayCapacityKbps 1024000 -PublicIPAddresses $publicIPAddresses -RedundantResourceCount 0 -GreVipSubnets $subnetVlansGreVip
	$fabricRole = Set-SCFabricRole -FabricRole $fabricRole -GatewayConfiguration $fabricRoleConfiguration

	# Get Service Instance 'SLB'
    $service = Get-SCService -Name "Gateway Manager"
    # Get RunAs Account 'NC_MgmtAdminRAA'
    $runAsAccount = Get-SCRunAsAccount -Name "NC_MgmtAdminRAA"
    $compTier = Get-SCComputerTier -Service $service
	
    $Transit = get-SCLogicalNetworkDefinition -Name "Transit_0"
    $subnetVlanIPv4 = New-SCSubnetVLan -Subnet  $Transit.SubnetVLans[0].Subnet -VLanID $Transit.SubnetVLans[0].VLanID
	
	foreach ($VM in $compTier.VMs)
	{
	   $vmFabricRoleResource = $fabricRole.ServiceVMs | where { $_.Resource -eq $VM }
	   Add-SCFabricRoleResource -FabricRole $fabricRole -VirtualMachine $VM -IPv4Subnet $subnetVlanIPv4 -RunAsAccount $runAsAccount
	
	}		

}


################################################################
#        Main Body to execute VMM Express                      #
################################################################

$VerbosePreference = "continue"
$ErrorActionPreference = "stop"
$NetworkControllerOnBoarder = $false
if ($psCmdlet.ParameterSetName -ne "NoParameters") {

    $global:stopwatch = [Diagnostics.Stopwatch]::StartNew()

    switch ($psCmdlet.ParameterSetName) 
    {
        "ConfigurationFile" {
            LogWrite "Using configuration from file [$ConfigurationDataFile]"
            $configdata = [hashtable] (iex (gc $ConfigurationDataFile | out-string))
        }
        "ConfigurationData" {
            LogWritee "Using configuration passed in from parameter"
            $configdata = $configurationData 
        }
    }
		
	try{
		# Get the node parameter from configdata 
		$node =  $configdata.AllNodes[0]
        if($node.UplinkPortProfile -eq "")
        {
            $node.UplinkPortProfile  = "NC_Uplink"
        }
		
		 # Get the VMM server. The connection to VMM server will be made by this    
		LogWrite "Getting VMM server connection with VMM server [$(gc env:computername)]"
        Write-Host "Getting VMM server connection with VMM server [$(gc env:computername)]"
		$VMMServer = Get-SCVMMServer -ComputerName localhost
		
		#check that all parameters are specified
        Write-Host "Checking the Fabric Configuration Input Parameters"
		checkParameters $node 	   
		
		#####################################################
		#STAGE 1: Create Management Logical Network         #
		#####################################################
		
		#check if the management Network is created or not
		
		$LNDName =''
		$ManagementVMNetwork
		$ManagementSubnet
		$LogicalNetworkCreated
		$VLANId
		
		if($node.IsManagementVMNetworkExisting -eq $false)
		{
			#Deploy Management Network and switch only if they are not deployed	
		 
            Write-Host "Logical Network and Logical Switch is not Pre-configured."
			foreach ($ln in $node.LogicalNetworks)
			{
				if($ln.Name -eq "NC_Management"){
			
					LogWrite "Starting to create Management Logical Network [$node.LogicalNetworkName]"
					
					#Create the logical Network
                    Write-Host "Creating Management Logical Network : [$node.LogicalNetworkName]"
					$LogicalNetworkCreated = createLogicalNetwork $node $ln $false
					
					#Create IP Pool for the created Management Logical Network
					$subnet = $ln.subnets[0]
					$ManagementSubnet = $subnet	
				   
					#Logical Network Definition Name 
					$LNDName = $ln.Name + "_0"   

					# Get the Management VM NetworkAdapter
					$ManagementVMNetwork = Get-SCVMNetwork -Name $ln.Name 

					#Get the VLANID
					$VLANId = $ln.subnets[0].VLANID 
					LogWrite " Logical Network creation succeeded"                               
				}
			}
		
			#####################################################################
			#STAGE 2: Create the Logical switch. This logical switch should be  #
            #         deployed on all the Hosts in this host group              #
			#####################################################################
		    Write-Host "Creating Logical Switch and Deploying to all Hosts in Host Group : [$node.NCHostGroupName]"    
			$logicalSwitchCreated = createLogicalSwitchAndDeployOnHosts $node $ManagementVMNetwork $LNDName $VLANId
		}	
		
		##########################################################################   
		#STAGE 3: Prepare the certificates if it has to be self signed. And copy #
        #         the required certificate in correspoding *.cr folder           # 
		#         so that the service template could be imported successfully    #                                                                
		##########################################################################

        
        if($node.IsCertSelfSigned -eq $true)
        {
            Write-Host "Generating Self-Signed Certificate.."
            generateSelfSignedCertificate $node
        }
        else
        {
            Write-Host "You have decided to use CA certificate. Hope you Placed the Cert in \Templates\NC\TrustedRootCertificate.cr folder "
        }
        $VMName = GetVMName $node
        LogWrite "Recieved VMName : [$VMName]"        
        LogWrite "VmName : [$VMName]"
						
		################################################    
		#STAGE 4: Import the service template into VMM #
		################################################
		
		importServiceTemplate $node 
				
		#########################################################
		#STAGE 5 : Configure the service template and deploy    #
		#########################################################
		
		configureAndDeployService $node $ManagementVMNetwork $ManagementSubnet
				
		###########################################################################################
		# STAGE 6 : On Board NC                                                                   #
		###########################################################################################	

        #sleep for 2 min so that netwrok controller deployment stables
        Start-Sleep -s 120
        #onboard network controller		
		OnBoardNetworkController $node $ManagementSubnet $VMName
        $NetworkControllerOnBoarder = $true
		
		#Onboard for 2 mins and then create HNVPA logical network Managed by NC
		Start-Sleep -s 120	

        ######################################################################
        # create other required logical networks which will be managed by NC #
        ######################################################################
        
        #create HNVPA
        $LogicalNetworkType = "HNVPA"
		$HNVPA = CreateLogicalNetworkWrapper $node $LogicalNetworkType $true
        AssociateLogicalNetWithUPP $LogicalNetworkType $node.UplinkPortProfile

        #create Transit Logical Network and associate the Logical network definition to NC uplink
        $LogicalNetworkType = "Transit"
        $HNVPA = CreateLogicalNetworkWrapper $node $LogicalNetworkType $true		
        AssociateLogicalNetWithUPP $LogicalNetworkType $node.UplinkPortProfile
	
        #Create Private VIP Logical Network
        $LogicalNetworkType = "PrivateVIP"
        $HNVPA = CreateLogicalNetworkWrapper $node $LogicalNetworkType $true
        
        #Create GREVIP
        $LogicalNetworkType = "GREVIP"
        $HNVPA = CreateLogicalNetworkWrapper $node $LogicalNetworkType $true
	
        #Create Public VIP logical netwrok
        $LogicalNetworkType = "PublicVIP"
        $HNVPA = CreateLogicalNetworkWrapper $node $LogicalNetworkType $true	        
		
		###############################################################
		#  Deploy and onboard SLB
		################################################################
		
        if($node.DeploySLB -eq $true)
        {	
			# Deploy SLB
			DeploySLB $node
			
			#Onboard SLB
			OnboardSLB $node
		}
		
		###############################################################
		#  Deploy and onboard Gateway
		################################################################
		if( $node.DeployGW -eq $true )
		{
            #Deploy Gateway
			DeployGateway $node	

            #Onboard gateway            
			OnboardGateway $node  
        }		
		
		
    }
	catch
	{       
            LogWrite " There is some Failure. Cleaning up the system to get in previous state..."    
            #cleanup the setup
            undoNCDeployment $node
	}
    
}