HNS.V2.psm1

#########################################################################
# Global Initialize

function Get-HnsClientNativeMethods()
{
        $signature = @'
        // Networks
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnEnumerateNetworks(
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Networks,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCreateNetwork(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr Network,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnOpenNetwork(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr Network,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnModifyNetwork(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Network,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnQueryNetworkProperties(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Network,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Properties,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnDeleteNetwork(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCloseNetwork(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Network);
 
        // Namespaces
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnEnumerateNamespaces(
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Namespaces,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCreateNamespace(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr Namespace,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnOpenNamespace(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr Namespace,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnModifyNamespace(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Namespace,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnQueryNamespaceProperties(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Namespace,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Properties,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnDeleteNamespace(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCloseNamespace(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Namespace);
 
        // Endpoint
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnEnumerateEndpoints(
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Endpoints,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCreateEndpoint(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Network,
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr Endpoint,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnOpenEndpoint(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr Endpoint,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnModifyEndpoint(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Endpoint,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnQueryEndpointProperties(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Endpoint,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Properties,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnDeleteEndpoint(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCloseEndpoint(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Endpoint);
 
        // LoadBalancer
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnEnumerateLoadBalancers(
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string LoadBalancers,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCreateLoadBalancer(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr LoadBalancer,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnOpenLoadBalancer(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr LoadBalancer,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnModifyLoadBalancer(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr LoadBalancer,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnQueryLoadBalancerProperties(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr LoadBalancer,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Properties,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnDeleteLoadBalancer(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCloseLoadBalancer(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr LoadBalancer);
 
        // SdnRoute
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnEnumerateSdnRoutes(
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Routes,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCreateSdnRoute(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr SdnRoute,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnOpenSdnRoute(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr SdnRoute,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnModifySdnRoute(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr SdnRoute,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnQuerySdnRouteProperties(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr SdnRoute,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Properties,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnDeleteSdnRoute(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCloseSdnRoute(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr SdnRoute);
 
        // Service
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnOpenService(
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr Service,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnRegisterServiceCallback(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Service,
            [MarshalAs(UnmanagedType.I4)]
            System.Int32 Callback,
            [MarshalAs(UnmanagedType.I4)]
            System.Int32 Context,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr CallbackHandle);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnUnregisterServiceCallback(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr CallbackHandle);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCloseService(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr Service);
 
        // Guest Network Service
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnEnumerateGuestNetworkServices(
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string GuestNetworkServices,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCreateGuestNetworkService(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr GuestNetworkService,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnOpenGuestNetworkService(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.SysUInt)]
            out IntPtr GuestNetworkService,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnModifyGuestNetworkService(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr GuestNetworkService,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Settings,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnQueryGuestNetworkServiceProperties(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr GuestNetworkService,
            [MarshalAs(UnmanagedType.LPWStr)]
            string Query,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Properties,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnDeleteGuestNetworkService(
            [MarshalAs(UnmanagedType.LPStruct)]
            Guid Id,
            [MarshalAs(UnmanagedType.LPWStr)]
            out string Result);
 
        [DllImport("computenetwork.dll")]
        public static extern System.Int64 HcnCloseGuestNetworkService(
            [MarshalAs(UnmanagedType.SysUInt)]
            IntPtr GuestNetworkService);
 
'@


    # Compile into runtime type
    Add-Type -MemberDefinition $signature -Namespace ComputeNetwork.HNS.PrivatePInvoke -Name NativeMethods -PassThru
}


Add-Type -TypeDefinition @"
    public enum ModifyRequestType
    {
        Add,
        Remove,
        Update,
        Refresh
    };
 
    public enum EndpointResourceType
    {
        Port,
        Policy,
    };
    public enum NetworkResourceType
    {
        DNS,
        Extension,
        Policy,
        Subnet,
        IPSubnet
    };
    public enum NamespaceResourceType
    {
    Container,
    Endpoint,
    };
"@


$ClientNativeMethods = Get-HnsClientNativeMethods

$NetworkNativeMethods = @{
    Open = $ClientNativeMethods::HcnOpenNetwork;
    Close = $ClientNativeMethods::HcnCloseNetwork;
    Enumerate = $ClientNativeMethods::HcnEnumerateNetworks;
    Delete = $ClientNativeMethods::HcnDeleteNetwork;
    Query = $ClientNativeMethods::HcnQueryNetworkProperties;
    Modify = $ClientNativeMethods::HcnModifyNetwork;
}

$EndpointNativeMethods = @{
    Open = $ClientNativeMethods::HcnOpenEndpoint;
    Close = $ClientNativeMethods::HcnCloseEndpoint;
    Enumerate = $ClientNativeMethods::HcnEnumerateEndpoints;
    Delete = $ClientNativeMethods::HcnDeleteEndpoint;
    Query = $ClientNativeMethods::HcnQueryEndpointProperties;
    Modify = $ClientNativeMethods::HcnModifyEndpoint;
}

$NamespaceNativeMethods = @{
    Open = $ClientNativeMethods::HcnOpenNamespace;
    Close = $ClientNativeMethods::HcnCloseNamespace;
    Enumerate = $ClientNativeMethods::HcnEnumerateNamespaces;
    Delete = $ClientNativeMethods::HcnDeleteNamespace;
    Query = $ClientNativeMethods::HcnQueryNamespaceProperties;
    Modify = $ClientNativeMethods::HcnModifyNamespace;
}

$LoadBalancerNativeMethods = @{
    Open = $ClientNativeMethods::HcnOpenLoadBalancer;
    Close = $ClientNativeMethods::HcnCloseLoadBalancer;
    Enumerate = $ClientNativeMethods::HcnEnumerateLoadBalancers;
    Delete = $ClientNativeMethods::HcnDeleteLoadBalancer;
    Query = $ClientNativeMethods::HcnQueryLoadBalancerProperties;
    Modify = $ClientNativeMethods::HcnModifyLoadBalancer;
}

$SdnRouteNativeMethods = @{
    Open = $ClientNativeMethods::HcnOpenSdnRoute;
    Close = $ClientNativeMethods::HcnCloseSdnRoute;
    Enumerate = $ClientNativeMethods::HcnEnumerateSdnRoutes;
    Delete = $ClientNativeMethods::HcnDeleteSdnRoute;
    Query = $ClientNativeMethods::HcnQuerySdnRouteProperties;
    Modify = $ClientNativeMethods::HcnModifySdnRoute;
}

$GuestNetworkServiceNativeMethods = @{
    Open = $ClientNativeMethods::HcnOpenGuestNetworkService;
    Close = $ClientNativeMethods::HcnCloseGuestNetworkService;
    Enumerate = $ClientNativeMethods::HcnEnumerateGuestNetworkServices;
    Delete = $ClientNativeMethods::HcnDeleteGuestNetworkService;
    Query = $ClientNativeMethods::HcnQueryGuestNetworkServiceProperties;
    Modify = $ClientNativeMethods::HcnModifyGuestNetworkService;
}

#########################################################################
# Generic implementation
#########################################################################

function Get-HnsGeneric
{
    param
    (
        [parameter(Mandatory=$false)] [Guid] $Id = [Guid]::Empty,
        [parameter(Mandatory=$false)] [Hashtable] $Filter = @{},
        [parameter(Mandatory=$false)] [Hashtable] $NativeMethods,
        [parameter(Mandatory=$false)] [switch]    $Detailed,
        [parameter(Mandatory=$false)] [int]       $Version
    )
    
    $ids = ""
    $FilterString = ConvertTo-Json $Filter -depth 10
    $query = @{Filter = $FilterString }
    if($Version -eq 2)
    {
        $query += @{SchemaVersion = @{ Major = 2; Minor = 0 }}
    }
    else
    {
        $query += @{SchemaVersion = @{ Major = 1; Minor = 0 }}
    }
    if($Detailed.IsPresent)
    {
        $query += @{Flags = 1}
    }
    $query = ConvertTo-Json $query
    if ($Id -ne [Guid]::Empty)
    {
        $ids = $Id
    }
    else
    {
        $result = ""
        $hr = $NativeMethods["Enumerate"].Invoke($query, [ref] $ids, [ref] $result);
        ReportErrors -FunctionName $NativeMethods["Enumerate"].Name -Hr $hr -Result $result -ThrowOnFail

        if($ids -eq $null)
        {
            return
        }

        $ids = ($ids | ConvertFrom-Json)
    }
    
    $output = @()
    $ids | ForEach-Object {
        $handle = 0
        $result = ""
        $hr = $NativeMethods["Open"].Invoke($_, [ref] $handle, [ref] $result);
        ReportErrors -FunctionName $NativeMethods["Open"].Name -Hr $hr -Result $result
        $properties = "";
        $result = ""
        $hr = $NativeMethods["Query"].Invoke($handle, $query, [ref] $properties, [ref] $result);
        ReportErrors -FunctionName $NativeMethods["Query"].Name -Hr $hr -Result $result
        $output += ConvertResponseFromJson -JsonInput $properties
        $hr = $NativeMethods["Close"].Invoke($handle);
        ReportErrors -FunctionName $NativeMethods["Close"].Name -Hr $hr
    }

    return $output
}

function Remove-HnsGeneric
{
    param
    (
        [parameter(Mandatory = $false, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True)]
        [Object[]] $InputObjects,
        [parameter(Mandatory=$false)] [Hashtable] $NativeMethods
    )

    begin {$objects = @()}
    process
    {
        if($InputObjects)
        {
            $Objects += $InputObjects;
        }
    }
    end {
        $Objects | Foreach-Object {
            $result = ""
            $hr = $NativeMethods["Delete"].Invoke($_.Id, [ref] $result);
            ReportErrors -FunctionName $NativeMethods["Delete"].Name -Hr $hr -Result $result
        }
    }
    
}

function Modify-HnsGeneric
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$false)] [Hashtable] $NativeMethods,
        [HashTable][parameter(Mandatory=$false)] $Settings
    )

    $result = ""
    # Get endpoint handle
    $handle = 0
    $hr = $NativeMethods["Open"].Invoke($Id, [ref] $handle, [ref] $result);
    ReportErrors -FunctionName $NativeMethods["Open"].Name -Hr $hr -Result $result
    try {
        $jsonString = (ConvertTo-Json  $Settings -Depth 10)
        Write-Verbose $jsonString
        $hr = $NativeMethods["Modify"].Invoke($handle, $jsonString, [ref] $result);
        ReportErrors -FunctionName $NativeMethods["Modify"].Name -Hr $hr -Result $result

    } finally {
        $hr = $NativeMethods["Close"].Invoke($handle);
        ReportErrors -FunctionName $NativeMethods["Close"].Name -Hr $hr
    }
}

#########################################################################
# Namespaces
#########################################################################
function New-HnsNamespace {
    param
    (
        [parameter(Mandatory = $false)] [Guid[]] $Endpoints = $null,
        [parameter(Mandatory = $false)] [switch] $Default
    )

    if([bool]$Default) {
        $namespace=@{Type= "HostDefault"; SchemaVersion = @{
                "Minor" = 2
                "Major" = 2
        }}
    }
    else
    {
        $namespace=@{ SchemaVersion = @{
                "Minor" = 2
                "Major" = 2
        }}
    }

    $id = [Guid]::Empty
    $settings = (ConvertTo-Json  $namespace -Depth 10)
    $handle = 0
    $result = ""
    $hnsClientApi = Get-HnsClientNativeMethods
    $hr = $hnsClientApi::HcnCreateNamespace($id, $settings, [ref] $handle, [ref] $result);
    ReportErrors -FunctionName HcnCreateNamespace -Hr $hr -Result $result -ThrowOnFail

    $query = '{"SchemaVersion": { "Major": 1, "Minor": 0 }}'
    $properties = "";
    $result = ""
    $hr = $hnsClientApi::HcnQueryNamespaceProperties($handle, $query, [ref] $properties, [ref] $result);
    ReportErrors -FunctionName HcnQueryNamespaceProperties -Hr $hr -Result $result
    $hr = $hnsClientApi::HcnCloseNamespace($handle);
    ReportErrors -FunctionName HcnCloseNamespace -Hr $hr

    $output = ConvertResponseFromJson -JsonInput $properties

    if($Endpoints -ne $null)
    {
        Foreach ($endpoint in $endpoints)
        {
            $Settings = @{EndpointId = $endpoint}
            $requestType = [ModifyRequestType]::Add
            $resourceType = [NamespaceResourceType]::Endpoint
            Modify-HnsNamespace -ID $output.Id -Settings $Settings -RequestType $requestType -ResourceType $resourceType 
        }
    }

    return $output
}

function Get-HnsNamespace
{
    param
    (
        [parameter(Mandatory=$false)] [Guid] $Id = [Guid]::Empty,
        [parameter(Mandatory=$false)] [int] $Version,
        [parameter(Mandatory=$false)] [switch] $Detailed
    )
    if ( $Detailed.IsPresent)
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $NamespaceNativeMethods  -Version $Version -Detailed
    }
    else
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $NamespaceNativeMethods  -Version $Version
    }
}

function Remove-HnsNamespace
{
    param
    (
        [parameter(Mandatory = $true, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True)]
        [Object[]] $InputObjects
    )
    begin {$objects = @()}
    process {$Objects += $InputObjects;}
    end {
        Remove-HnsGeneric -InputObjects $Objects -NativeMethods $NamespaceNativeMethods
    }
}

function Modify-HnsNamespace
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$true)] [ModifyRequestType] $RequestType,
        [parameter(Mandatory=$false)] [NamespaceResourceType] $ResourceType,
        [HashTable][parameter(Mandatory=$false)] $Settings
    )

    $msettings = @{
        RequestType = "$RequestType";
        ResourceType = "$ResourceType";
    }

    if ($Settings)
    {
        $msettings += @{
            Settings = $Settings;
        }
    }

    return Modify-HnsGeneric -Id $Id -NativeMethods $NamespaceNativeMethods -Settings $msettings
}

#########################################################################
# LoadBalancer
#########################################################################
Add-Type -TypeDefinition @"
    [System.Flags]
    public enum LoadBalancerDistribution
    {
        None = 0,
        SourceIPProtocol = 1,
        SourceIP = 2,
    };
 
    [System.Flags]
    public enum LoadBalancerFlags
    {
        None = 0,
        EnableDirectServerReturn = 1,
    }
 
    [System.Flags]
    public enum LoadBalancerPortMappingFlags
    {
        None = 0,
        EnableInternalLoadBalancer = 1,
        LocalRoutedVip = 2,
        EnablePreserveDip = 8,
    }
"@

function New-HnsLoadBalancer {
    param
    (
        [parameter(Mandatory = $false)] [Guid[]] $Endpoints = $null,
        [parameter(Mandatory = $true)] [int] $InternalPort,
        [parameter(Mandatory = $true)] [int] $ExternalPort,
        [parameter(Mandatory = $true)] [int] $Protocol,
        [parameter(Mandatory = $false)] [string] $Vip,
        [parameter(Mandatory = $false)] [string] $SourceVip,
        [parameter(Mandatory = $false)] [switch] $LocalRoutedVip,
        [parameter(Mandatory = $false)] [switch] $ILB,
        [parameter(Mandatory = $false)] [switch] $DSR,
        [parameter(Mandatory = $false)] [switch] $PreserveDip,
        [parameter(Mandatory = $false)] [string] $LoadBalancerDistribution
    )

    $portMapping = @{}
    $portMapping.InternalPort = $InternalPort
    $portMapping.ExternalPort = $ExternalPort
    $portMapping.Protocol = $Protocol
    $portMapping.DistributionType = [LoadBalancerDistribution]::None;
    if ($LoadBalancerDistribution -eq "SourceIPProtocol")
    {
        $portMapping.DistributionType = [LoadBalancerDistribution]::SourceIPProtocol
    }
    elseif ($LoadBalancerDistribution -eq "SourceIP")
    {
        $portMapping.DistributionType = [LoadBalancerDistribution]::SourceIP
    }
    $portmapping.Flags = [LoadBalancerPortMappingFlags]::None;
    if($ILB.IsPresent)
    {
        $portmapping.Flags = $portmapping.Flags -bor [LoadBalancerPortMappingFlags]::EnableInternalLoadBalancer;
    }
    if($LocalRoutedVip.IsPresent)
    {
        $portmapping.Flags = $portmapping.Flags -bor [LoadBalancerPortMappingFlags]::LocalRoutedVip;
    }
    if($DSR.IsPresent)
    {
        if($PreserveDip.IsPresent)
        {
            $portmapping.Flags = $portmapping.Flags -bor [LoadBalancerPortMappingFlags]::EnablePreserveDip;
        }
    }


    $LoadBalancers = @{
        HostComputeEndpoints = @(
            $Endpoints;
        );
        PortMappings = @(
            $portMapping
        );
        FrontendVIPs = @(
        );
        Flags = [LoadBalancerFlags]::None;
        SchemaVersion = @{
                "Minor" = 2
                "Major" = 2
        }
    SourceVIP = $SourceVip
    }

    if($DSR.IsPresent)
    {
        $LoadBalancers.Flags = $LoadBalancers.Flags -bor [LoadBalancerFlags]::EnableDirectServerReturn;
    }

    if(-not [String]::IsNullOrEmpty($vip))
    {
        $LoadBalancers.FrontendVIPs += $Vip
    }

    

    $id = [Guid]::Empty
    $settings = (ConvertTo-Json  $LoadBalancers -Depth 10) 
    $handle = 0
    $result = ""
    $hnsClientApi = Get-HnsClientNativeMethods
    $hr = $hnsClientApi::HcnCreateLoadBalancer($id, $settings, [ref] $handle, [ref] $result);
    ReportErrors -FunctionName HcnCreateLoadBalancer -Hr $hr -Result $result -ThrowOnFail

    $query = '{"SchemaVersion": { "Major": 1, "Minor": 0 }}'
    $properties = "";
    $result = ""
    $hr = $hnsClientApi::HcnQueryLoadBalancerProperties($handle, $query, [ref] $properties, [ref] $result);
    ReportErrors -FunctionName HcnQueryLoadBalancerProperties -Hr $hr -Result $result
    $hr = $hnsClientApi::HcnCloseLoadBalancer($handle);
    ReportErrors -FunctionName HcnCloseLoadBalancer -Hr $hr

    return ConvertResponseFromJson -JsonInput $properties
}

function Get-HnsPolicyList
{
    param
    (
        [parameter(Mandatory = $false)] [string] $Id = [Guid]::Empty,
        [parameter(Mandatory = $false)] [switch] $Detailed
    )
    if ($Detailed.IsPresent)
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $LoadBalancerNativeMethods -Detailed
    }
    else
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $LoadBalancerNativeMethods
    }
}

function Remove-HnsPolicyList
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$true,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
        [Object[]] $InputObjects
    )
    begin {$objects = @()}
    process {$Objects += $InputObjects;}
    end {
        Remove-HnsGeneric -InputObjects $Objects -NativeMethods $LoadBalancerNativeMethods
    }
}

function Get-HnsLoadBalancer
{
    param
    (
        [parameter(Mandatory = $false)] [Guid] $Id = [Guid]::Empty,
        [parameter(Mandatory=$false)] [int] $Version,
        [parameter(Mandatory=$false)] [switch] $Detailed
    )
    if ($Detailed.IsPresent)
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $LoadBalancerNativeMethods -Version $Version -Detailed
    }
    else
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $LoadBalancerNativeMethods -Version $Version
    }
}

function Remove-HnsLoadBalancer
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$true,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
        [Object[]] $InputObjects
    )
    begin {$objects = @()}
    process {$Objects += $InputObjects;}
    end {
        Remove-HnsGeneric -InputObjects $Objects -NativeMethods $LoadBalancerNativeMethods
    }
}

function Modify-HnsLoadBalancer
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$true)] [ModifyRequestType] $RequestType,
        [parameter(Mandatory=$false)] [LoadBalancerResourceType] $ResourceType,
        [HashTable][parameter(Mandatory=$false)] $Settings
    )

    $msettings = @{
        RequestType = "$RequestType";
        ResourceType = "$ResourceType";
    }

    if ($Settings)
    {
        $msettings += @{
            Settings = $Settings;
        }
    }

    return Modify-HnsGeneric -Id $Id -NativeMethods $LoadBalancerNativeMethods -Settings $msettings
}

#########################################################################
# SdnRoute
#########################################################################
function New-HnsRoute {
    param
    (
        [parameter(Mandatory = $false)] [Guid[]] $Endpoints = $null,
        [parameter(Mandatory = $true)] [string] $DestinationPrefix,
        [parameter(Mandatory = $false)] [switch] $EncapEnabled,
        [parameter(Mandatory = $false)] [string] $NextHop,
        [parameter(Mandatory = $false)] [switch] $MonitorDynamicEndpoints
    )

    $SDNRoutePolicySetting = @{
        DestinationPrefix = $DestinationPrefix;
        NextHop = $NextHop;
        NeedEncap = $EncapEnabled.IsPresent;
        AutomaticEndpointMonitor = $MonitorDynamicEndpoints.IsPresent;
    }

    $HostComputeRoute = @{
        HostComputeEndpoints = @(
            $Endpoints;
        );
        Routes = @(
            $SDNRoutePolicySetting
        );
        SchemaVersion = @{
                "Minor" = 2
                "Major" = 2
        }
    }

    $id = [Guid]::Empty
    $settings = (ConvertTo-Json  $HostComputeRoute -Depth 10) 
    $handle = 0
    $result = ""
    $hnsClientApi = Get-HnsClientNativeMethods
    $hr = $hnsClientApi::HcnCreateSdnRoute($id, $settings, [ref] $handle, [ref] $result);
    ReportErrors -FunctionName HcnCreateSdnRoute -Hr $hr -Result $result -ThrowOnFail

    $query = '{"SchemaVersion": { "Major": 1, "Minor": 0 }}'
    $properties = "";
    $result = ""
    $hr = $hnsClientApi::HcnQuerySdnRouteProperties($handle, $query, [ref] $properties, [ref] $result);
    ReportErrors -FunctionName HcnQuerySdnRouteProperties -Hr $hr -Result $result
    $hr = $hnsClientApi::HcnCloseSdnRoute($handle);
    ReportErrors -FunctionName HcnCloseSdnRoute -Hr $hr

    return ConvertResponseFromJson -JsonInput $properties
}

function Get-HnsRoute
{
    param
    (
        [parameter(Mandatory = $false)] [Guid] $Id = [Guid]::Empty,
        [parameter(Mandatory=$false)] [int] $Version,
        [parameter(Mandatory=$false)] [switch] $Detailed
    )
    if ($Detailed.IsPresent)
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $SdnRouteNativeMethods -Version $Version -Detailed
    }
    else
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $SdnRouteNativeMethods -Version $Version
    }
}

function Remove-HnsRoute
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$true,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
        [Object[]] $InputObjects
    )
    begin {$objects = @()}
    process {$Objects += $InputObjects;}
    end {
        Remove-HnsGeneric -InputObjects $Objects -NativeMethods $SdnRouteNativeMethods
    }
}

#########################################################################
# Networks
#########################################################################
# Add missing Network types if necessary
if (-Not ("NetworkFlags" -as [type])) 
{
    Add-Type -TypeDefinition @"
        [System.Flags]
        public enum NetworkFlags
        {
            None = 0,
            EnableDns = 1,
            EnableDhcp = 2,
            EnableMirroring = 4,
            EnableNonPersistent = 8,
            EnablePersistent = 16
        }
 
        [System.Flags]
        public enum EndpointFlags
        {
            None = 0,
            RemoteEndpoint = 1,
            DisableICC = 2,
            EnableMirroring = 4,
            EnableLowInterfaceMetric = 8,
            OverrideDNSServerOrder = 16,
            EnableDhcp = 32
        }
 
        [System.Flags]
        public enum IPSubnetFlags
        {
            None = 0,
            EnableBroadcast = 1,
            ReserveNetworkAddress = 2,
        }
"@

}

function New-HnsIcsNetwork
{
    param
    (
        [parameter(Mandatory = $false)] [string] $Name,
        [parameter(Mandatory = $false)] [string] $AddressPrefix,
        [parameter(Mandatory = $false)] [string] $Gateway,
        [parameter(Mandatory= $false)] [NetworkFlags] $NetworkFlags,
        [parameter(Mandatory= $false)] [int] $Vlan = 0,
        [parameter(Mandatory = $false)] [string] $DNSServer,
        [parameter(Mandatory = $false)] [int]    $ICSFlags = 0,
        [parameter(Mandatory = $false)] [string] $InterfaceConstraint = $null
    )
    $NetworkSpecificParams = @{
    }

    if ($InterfaceConstraint)
    {
        $NetworkSpecificParams.Policies += @{ 
            "Type" = "INTERFACECONSTRAINT";
            "Settings" = $InterfaceConstraint;
        }
    }

    $spolicy = @{}


    $NetworkSpecificParams += @{
        Flags = $NetworkFlags
    }

    return new-hnsnetwork -type ics `
        -Name $Name -AddressPrefix $AddressPrefix -Gateway $Gateway `
        -DNSServer $DNSServer `
        -AdditionalParams @{"ICSFlags" = $ICSFlags } `
        -NetworkSpecificParams $NetworkSpecificParams `
        -vlan $vlan
}

function New-HnsDefaultSwitchNetwork
{
    param
    (
        [parameter(Mandatory = $false)] [string] $AddressPrefix,
        [parameter(Mandatory = $false)] [string] $Gateway,
        [parameter(Mandatory = $false)] [string] $SubnetAddressPrefix
    )

    $sharedAccess = Get-Service -Name SharedAccess -ErrorAction SilentlyContinue
    if (-not $sharedAccess)
    {
        throw $("The required service 'SharedAccess' is not present on this system.") 
    }

    if ($sharedAccess.StartType -ne [System.ServiceProcess.ServiceStartMode]::Automatic)
    {
        Set-Service -Name SharedAccess -Startuptype Automatic
    }

    $NetworkSpecificParams = @{
        ID = "c08cb7b8-9b3c-408e-8e30-5e16a3aeb444"
        Flags = ([NetworkFlags]::EnableDhcp -bor [NetworkFlags]::EnableDns -bor [NetworkFlags]::EnablePersistent)
    }

    $IpSubnet = @{
        IpAddressPrefix = $SubnetAddressPrefix
        Flags = 0
    }
    $IpSubnets = @($IpSubnet)

    return new-hnsnetwork -Type ics `
                          -Name "Default Switch" `
                          -AddressPrefix $AddressPrefix `
                          -Gateway $Gateway `
                          -IPSubnets $IpSubnets `
                          -NetworkSpecificParams $NetworkSpecificParams
}

function New-HnsNetwork
{
    param
    (
        [parameter(Mandatory=$false, Position=0)]
        [string] $JsonString,
        [ValidateSet('ICS', 'Internal', 'Transparent', 'NAT', 'Overlay', 'L2Bridge', 'L2Tunnel', 'Layered', 'Private')]
        [parameter(Mandatory = $false, Position = 0)]
        [string] $Type,
        [parameter(Mandatory = $false)] [string] $Name,
        [parameter(Mandatory = $false)] $AddressPrefix,
        [parameter(Mandatory = $false)] $IPSubnets, # @(@{"IpAddressPrefix"="192.168.1.0/24";"Flags"=0},@{"IpAddressPrefix"="192.168.2.0/24";"Flags"=0})
        [parameter(Mandatory = $false)] $Gateway,
        [parameter(Mandatory= $false)] [int] $Vlan = 0,
        [parameter(Mandatory= $false)] [int] $Vsid = 0,
        [parameter(Mandatory = $false)] [switch] $IPv6,
        [parameter(Mandatory = $false)] [string] $DNSServer,
        [parameter(Mandatory = $false)] [string] $AdapterName,
        [HashTable][parameter(Mandatory=$false)] $AdditionalParams, # @ {"ICSFlags" = 0; }
        [HashTable][parameter(Mandatory=$false)] $NetworkSpecificParams, # @ {"InterfaceConstraint" = ""; }
        [parameter(Mandatory = $false)] [int] $VxlanPort = 0,
        [parameter(Mandatory = $false)] [bool] $AutomaticDnsEnabled = $false
    )

    Begin {
        if (!$JsonString) {
            $netobj = @{
                Type = $Type;
                Policies = @();
            };

            if ($Name) {
                $netobj += @{
                    Name = $Name;
                }
            }

            if ($NetworkSpecificParams) {
                $netobj += $NetworkSpecificParams
            }
            # Coalesce prefix/gateway into subnet objects.
            if ($AddressPrefix) {
                $ipams = @()
                $ipam = @{
                    Type = "Static";
                }

                $subnets += @()
                $prefixes = @($AddressPrefix)
                $gateways = @($Gateway)

                $len = $prefixes.length
                for ($i = 0; $i -lt $len; $i++) {
                    $subnet = @{ IpAddressPrefix = $prefixes[$i]; }
                    $routes = @()
                    if ($i -lt $gateways.length -and $gateways[$i]) {
                        $routes += @{ 
                            NextHop = $gateways[$i]; 
                            DestinationPrefix = "0.0.0.0/0";
                        }         
                    }
                    $subnet +=  @{
                        Routes = $routes
                    }
                    $Subnet.Policies = @() 
                    if ($vlan -gt 0) {
                        $subnet.Policies += @{"Type"= "VLAN"; "Settings" = @{"IsolationId" = $VLAN}}
                    }
                    if ($Vsid -gt 0) {
                        $subnet.Policies += @{"Type"= "VSID"; "Settings" = @{"IsolationId" = $VSID}}
                    }

                    $Subnet.IpSubnets = $IPSubnets

                    $subnets += $subnet
                }

                $ipam += @{
                    Subnets = $subnets;
                }
                $ipams += $ipam;
                $netobj += @{
                    Ipams = $ipams;
                }
            }
            if ($IPv6.IsPresent) {
                $netobj += @{ IPv6 = $true }
            }

            if ($AdapterName) {
                $netobj.Policies += @{"Type"= "NetAdapterName"; "Settings" = @{"NetworkAdapterName" = $AdapterName}}
            }

            if ($DNSServer) {
                $list = $DNSServer -split ","
                $netobj += @{Dns = @{ ServerList = $list}}
            }

            if ($AdditionalParams) {
                $netobj += @{
                    AdditionalParams = @{}
                }

                foreach ($param in $AdditionalParams.Keys) {
                    $netobj.AdditionalParams += @{
                        $param = $AdditionalParams[$param];
                    }
                }
            }

            if ($VxlanPort -gt 0) {
                $netobj.Policies += @{ "Type" = "VxlanPort"; "Settings" = @{ "Port" = $VxlanPort }}
            }

            if ($AutomaticDnsEnabled)
            {
                $netobj.Policies += @{ "Type" = "AutomaticDNS"; "Settings" = @{ "Enable" = $true }}
            }

            $netobj.SchemaVersion += @{
                "Minor" = 2
                "Major" = 2
        }
            $JsonString = ConvertTo-Json $netobj -Depth 10
        }

    }
    Process{
        $id = [Guid]::Empty
        $settings = $JsonString
        $handle = 0
        $result = ""
        $hnsClientApi = Get-HnsClientNativeMethods
        $hr = $hnsClientApi::HcnCreateNetwork($id, $settings, [ref] $handle, [ref] $result);
        ReportErrors -FunctionName HcnCreateNetwork -Hr $hr -Result $result -ThrowOnFail

        $query =  '{"SchemaVersion": { "Major": 1, "Minor": 0 }}'
        $properties = "";
        $result = ""
        $hr = $hnsClientApi::HcnQueryNetworkProperties($handle, $query, [ref] $properties, [ref] $result);
        ReportErrors -FunctionName HcnQueryNetworkProperties -Hr $hr -Result $result
        $hr = $hnsClientApi::HcnCloseNetwork($handle);
        ReportErrors -FunctionName HcnCloseNetwork -Hr $hr

        return ConvertResponseFromJson -JsonInput $properties
    }
}

function Get-HnsNetwork
{
    param
    (
        [parameter(Mandatory=$false)] [Guid] $Id = [Guid]::Empty,
        [parameter(Mandatory=$false)] [switch] $Detailed,
        [parameter(Mandatory=$false)] [int] $Version
    )
    if($Detailed.IsPresent)
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $NetworkNativeMethods -Version $Version -Detailed
    }
    else
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $NetworkNativeMethods -Version $Version
    }
}

function Modify-HnsNetworkDNS
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$false)] [String] $Domain = "",
        [parameter(Mandatory=$false)] [String[]] $Search = "",
        [parameter(Mandatory=$false)] [String[]] $ServerList = "",
        [parameter(Mandatory=$false)] [String[]] $Options = "",
        [parameter(Mandatory=$true)] [ModifyRequestType] $RequestType,
        [parameter(Mandatory=$false)] [NetworkResourceType] $ResourceType
    )
    $settings = @{
        Domain = $Domain;
        Search = $Search;
        ServerList = $ServerList;
        Options = $Options;
    }
    Modify-HnsNetwork -Id $Id -RequestType $RequestType -ResourceType $ResourceType -Settings $Settings
}

function Update-HnsNetworkDNS
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$false)] [String] $Domain = "",
        [parameter(Mandatory=$false)] [String[]] $Search = @(),
        [parameter(Mandatory=$false)] [String[]] $ServerList = @(),
        [parameter(Mandatory=$false)] [String[]] $Options = @()
    )
    $RequestType = [ModifyRequestType]::Update
    $ResourceType = [NetworkResourceType]::DNS
    Modify-HnsNetworkDNS -Id $Id -RequestType $RequestType -ResourceType $ResourceType -Domain `
      $Domain -Search $Search -ServerList $ServerList -Options $Options
}

function Update-HnsNetworkExtension
{
    param
    (
        [parameter (Mandatory=$true)] [Guid] $Id,
        [parameter (Mandatory=$false)] [Guid] $ExtensionId,
        [parameter (Mandatory=$false)] [bool] $IsEnabled
    )
    $RequestType = [ModifyRequestType]::Update
    $ResourceType = [NetworkResourceType]::Extension
    $settings = @{
               Id = $ExtensionId;
               IsEnabled = $IsEnabled;
    }
    Modify-HnsNetwork -Id $Id -RequestType $RequestType -ResourceType $ResourceType -Settings $Settings
}

function New-HnsNetworkPolicy
{
    param
    (
        [parameter(Mandatory = $false)] [Guid] $NetworkId = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $Policies
    )
    $requestType = [ModifyRequestType]::Add
    $resourceType = [NetworkResourceType]::Policy
    Modify-HnsNetwork -Id $NetworkId -RequestType $requestType -ResourceType $resourceType -PolicyArray $Policies
}
function Remove-HnsNetworkPolicy
{
    param
    (
        [parameter(Mandatory = $false)] [Guid] $NetworkId = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $Policies
    )
    $requestType = [ModifyRequestType]::Remove
    $resourceType = [NetworkResourceType]::Policy
    Modify-HnsNetwork -Id $NetworkId -RequestType $requestType -ResourceType $resourceType -PolicyArray $Policies
}

function New-HnsRemoteSubnetRoutePolicy
{
    param
    (
        [parameter (Mandatory = $true)] [Guid] $NetworkId,
        [parameter (Mandatory = $true)] [string] $DestinationPrefix,
        [parameter (Mandatory = $true)] [int] $IsolationId,
        [parameter (Mandatory = $true)] [string] $ProviderAddress,
        [parameter (Mandatory = $true)] [string] $DistributedRouterMacAddress
    )
    $Type = "RemoteSubnetRoute";
    $rsPolicy = @{
                DestinationPrefix = $DestinationPrefix;
                IsolationId = $IsolationId;
                ProviderAddress = $ProviderAddress;
                DistributedRouterMacAddress = $DistributedRouterMacAddress;
    }
    $settings = @{
        Type = $Type;
        Settings = $rsPolicy;
    }    
    New-HnsNetworkPolicy -NetworkId $NetworkId -Policies @($Settings)
}

function Remove-HnsRemoteSubnetRoutePolicy
{
    param
    (
        [parameter (Mandatory = $true)] [Guid] $NetworkId,
        [parameter (Mandatory = $true)] [string] $DestinationPrefix,
        [parameter (Mandatory = $true)] [int] $IsolationId,
        [parameter (Mandatory = $true)] [string] $ProviderAddress,
        [parameter (Mandatory = $true)] [string] $DistributedRouterMacAddress
    )
    $Type = "RemoteSubnetRoute";
    $rsPolicy = @{
        DestinationPrefix = $DestinationPrefix;
        IsolationId = $IsolationId;
        ProviderAddress = $ProviderAddress;
        DistributedRouterMacAddress = $DistributedRouterMacAddress;
}
    $settings = @{
        Type = $Type;
        Settings = $rsPolicy;
    }
    Remove-HnsNetworkPolicy -NetworkId $NetworkId -Policies @($Settings)
}

function New-HnsHostRoutePolicy
{
    param
    (
        [parameter (Mandatory = $true)] [Guid] $NetworkId
    )

    $Type = "HostRoute";
    $rsPolicy = @{
    }
    $settings = @{
        Type = $Type;
        Settings = $rsPolicy;
    }    
    New-HnsNetworkPolicy -NetworkId $NetworkId -Policies @($Settings)
}

function Remove-HnsHostRoutePolicy
{
    param
    (
        [parameter (Mandatory = $true)] [Guid] $NetworkId
    )

    $Type = "HostRoute";
    $rsPolicy = @{
    }
    $settings = @{
        Type = $Type;
        Settings = $rsPolicy;
    }
    Remove-HnsNetworkPolicy -NetworkId $NetworkId -Policies @($Settings)
}

function New-HnsSubnet
{
    param
    (
        [parameter(Mandatory = $false)] [Guid] $NetworkId = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $Subnets
    )
    $requestType = [ModifyRequestType]::Add
    $resourceType = [NetworkResourceType]::Subnet
    Modify-HnsNetwork -Id $NetworkId -RequestType $requestType -ResourceType $resourceType -SubnetArray $Subnets
}

function Remove-HnsSubnet
{
    param
    (
        [parameter(Mandatory = $false)] [Guid] $NetworkId = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $Subnets
    )
    $requestType = [ModifyRequestType]::Remove
    $resourceType = [NetworkResourceType]::Subnet
    Modify-HnsNetwork -Id $NetworkId -RequestType $requestType -ResourceType $resourceType -SubnetArray $Subnets
}

function New-HnsIPSubnet
{
    param
    (
        [parameter(Mandatory = $false)] [Guid] $NetworkId = $null,
        [parameter(Mandatory = $false)] [Guid] $SubnetId = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $IPSubnets
    )
    $requestType = [ModifyRequestType]::Add
    $resourceType = [NetworkResourceType]::IPSubnet
    Modify-HnsNetwork -Id $NetworkId -RequestType $requestType -ResourceType $resourceType -SubnetId $SubnetId -IPSubnetArray $IPSubnets
}

function Remove-HnsIPSubnet
{
    param
    (
        [parameter(Mandatory = $false)] [Guid] $NetworkId = $null,
        [parameter(Mandatory = $false)] [Guid] $SubnetId = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $IPSubnets
    )
    $requestType = [ModifyRequestType]::Remove
    $resourceType = [NetworkResourceType]::IPSubnet
    Modify-HnsNetwork -Id $NetworkId -RequestType $requestType -ResourceType $resourceType -SubnetId $SubnetId -IPSubnetArray $IPSubnets
}

function Remove-HnsNetwork
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$true,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
        [Object[]] $InputObjects
    )
    begin {$objects = @()}
    process {$Objects += $InputObjects;}
    end {
        Remove-HnsGeneric -InputObjects $Objects -NativeMethods $NetworkNativeMethods
    }
}

function Modify-HnsNetwork
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$true)] [ModifyRequestType] $RequestType,
        [parameter(Mandatory=$false)] [NetworkResourceType] $ResourceType,
        [HashTable][parameter(Mandatory=$false)] $Settings,
        [HashTable[]][parameter(Mandatory=$false)] $PolicyArray,
        [HashTable[]][parameter(Mandatory=$false)] $SubnetArray,
        [parameter(Mandatory=$false)] $SubnetId,
        [HashTable[]][parameter(Mandatory=$false)] $IPSubnetArray
    )

    $msettings = @{
        RequestType = "$RequestType";
        ResourceType = "$ResourceType";
    }

    if ($Settings)
    {
        $msettings += @{
            Settings = $Settings;
        }
    }
    elseif($PolicyArray)
    {
        $policies = @{
            Policies = $PolicyArray;
        }
        $msettings += @{
            Settings = $policies;
        }
    }

    if($SubnetArray)
    {
        $Subnets = @{
            Subnets = $SubnetArray;
        }
        $msettings += @{
            Settings = $Subnets;
        }
    }

    if($IPSubnetArray)
    {
        $IPSubnets = @{
            SubnetId = $SubnetId
            IpSubnets = $IPSubnetArray;
        }
        $msettings += @{
            Settings = $IPSubnets;
        }
    }

    return Modify-HnsGeneric -Id $Id -NativeMethods $NetworkNativeMethods -Settings $msettings
}
#########################################################################
# Endpoints
#########################################################################
Add-Type -TypeDefinition @"
    [System.Flags]
    public enum NatFlags
    {
        None = 0,
        LocalRoutedVip = 1,
    }
"@


function New-HnsEndpoint
{
    param
    (
        [parameter(Mandatory=$false, Position = 0)] [string] $JsonString = $null,
        [parameter(Mandatory = $true, Position = 0)] [Guid] $NetworkId,
        [parameter(Mandatory = $false)] [string] $Name,
        [parameter(Mandatory = $false)] [string] $IPAddress,
        [parameter(Mandatory = $false)] [uint16] $PrefixLength,
        [parameter(Mandatory = $false)] [string] $IPv6Address,
        [parameter(Mandatory = $false)] [uint16] $IPv6PrefixLength,
        [parameter(Mandatory = $false)] [string] $GatewayAddress,
        [parameter(Mandatory = $false)] [string] $GatewayAddressV6,
        [parameter(Mandatory = $false)] [string] $DNSServerList,
        [parameter(Mandatory = $false)] [string] $MacAddress,
        [parameter(Mandatory = $false)] [switch] $RemoteEndpoint,
        [parameter(Mandatory = $false)] [switch] $EnableOutboundNat,
        [HashTable][parameter(Mandatory=$false)] $OutboundNatPolicy, # @ {"LocalRoutedVip" = true; "VIP" = ""; ExceptionList = ["", ""]}
        [parameter(Mandatory = $false)] [string[]] $OutboundNatExceptions,
        [parameter(Mandatory = $false)] [string[]] $RoutePrefixes, # Deprecate this. use RoutePolicies
        [HashTable[]][parameter(Mandatory=$false)] $RoutePolicies, # @( @ {"DestinationPrefix" = ""; "NeedEncap" = true; "NextHop" = ""} )
        [HashTable][parameter(Mandatory=$false)] $InboundNatPolicy, # @ {"InternalPort" = "80"; "ExternalPort" = "8080"}
        [HashTable][parameter(Mandatory=$false)] $PAPolicy, # @ {"PA" = "1.2.3.4"; }
        [parameter(Mandatory = $false)] [switch] $UseInternalDns
    )

    begin
    {
        if ($JsonString)
        {
            $EndpointData = $JsonString | ConvertTo-Json | ConvertFrom-Json
        }
        else
        {
            $endpoint = @{
                HostComputeNetwork = $NetworkId;
                Policies       = @();
                SchemaVersion = @{
                    "Minor" = 2;
                    "Major" = 2;
                };
            }

            if ($Name) {
                $endpoint += @{
                    Name = $Name;
                }
            }

            if ($MacAddress) {
                $endpoint += @{
                    MacAddress = $MacAddress;
                }
            }
            $IpConfigurations = @();
            $Routes = @()         
            
            if ($IPAddress -Or $PrefixLength) {
                $IpConfiguration = @{
                }

                if ($IPAddress) {
                    $IpConfiguration.IpAddress = $IPAddress;
                }

                if ($PrefixLength) {
                    $IpConfiguration.PrefixLength = $PrefixLength;
                }
                $IpConfigurations += $IpConfiguration;
            }

            if ($GatewayAddress) {
                $Routes += @{ NextHop = $GatewayAddress; DestinationPrefix = "0.0.0.0/0"}
            }
           
            if ($IPv6Address -Or $IPv6PrefixLength) {
                $IpConfiguration = @{
                }

                if ($IPv6Address) {
                    $IpConfiguration.IpAddress = $IPv6Address;
                }

                if ($IPv6PrefixLength) {
                    $IpConfiguration.PrefixLength = $IPv6PrefixLength;
                }
                $IpConfigurations += $IpConfiguration;
            }
            
            if ($GatewayAddressV6) {
                $Routes += @{ NextHop = $GatewayAddressV6; DestinationPrefix = "::/0"}
            }

            $endpoint += @{IpConfigurations = $IpConfigurations}
            $endpoint += @{Routes = $Routes}
            
            if ($DNSServerList) {
                $list = $DNSServerList -split ","
                $endpoint += @{Dns = @{ ServerList = $list}}
            }

            [EndpointFlags]$Flags = [EndpointFlags]::None;

            if ($RemoteEndpoint.IsPresent) {
                $Flags = $Flags -bor [EndpointFlags]::RemoteEndpoint;
            }

            if ($UseInternalDns.IsPresent) {
                $Flags = $Flags -bor [EndpointFlags]::OverrideDNSServerOrder;
            }

            if($Flags -ne [EndpointFlags]::None)
            {
                $endpoint += @{Flags= $Flags;}
            }

            if ($EnableOutboundNat.IsPresent) {
                $Settings = @{}
                if ($OutboundNatExceptions) {
                    $ExceptionList = @()
                    foreach ($exp in $OutboundNatExceptions)
                    {
                        $ExceptionList += $exp
                    }
                    $Settings += @{Exceptions = $ExceptionList}
                }

                $endpoint.Policies +=  @{
                    Type = "OutBoundNAT";
                    Settings = $Settings;
                };
            }

            if ($OutboundNatPolicy)
            {
                $natFlags = 0;
                if ($OutboundNatPolicy["LocalRoutedVip"])
                {
                    $natFlags = $natFlags -bor [NatFlags]::LocalRoutedVip
                }
                $settings = $OutboundNatPolicy
                $settings += @{
                    Flags = $natFlags;
                }

                $endpoint.Policies +=  @{
                    Type = "OutBoundNAT";
                    Settings = $settings;
                };
            }

            if ($RoutePolicies)
            {
                foreach ($routepolicy in $RoutePolicies)
                {
                    $rPolicy = @{
                        DestinationPrefix = $routepolicy["DestinationPrefix"];
                        NeedEncap = $true;
                    }
                    if ($routepolicy.ContainsKey("NextHop"))
                    {
                        $rPolicy.NextHop = $routepolicy["NextHop"]
                    }

                    $endpoint.Policies +=  @{
                        Type = "SDNRoute";
                        Settings = $rPolicy;
                    };
                }
            }

            # Deprecate this
            if ($RoutePrefixes)
            {
                foreach ($routeprefix in $RoutePrefixes) {
                    $rPolicy = @{
                        DestinationPrefix = $routeprefix;
                        NeedEncap = $true;
                    }
                    $endpoint.Policies +=  @{
                        Type = "SDNRoute";
                        Settings = $rPolicy;
                    };
                }
            }

            if ($InboundNatPolicy) {
                $natFlags = 0;
                if ($InboundNatPolicy["LocalRoutedVip"])
                {
                    $natFlags = $natFlags -bor [NatFlags]::LocalRoutedVip
                }

                $endpoint.Policies += @{
                    Type = "PortMapping";
                    Settings = @{
                        InternalPort = $InboundNatPolicy["InternalPort"];
                        ExternalPort = $InboundNatPolicy["ExternalPort"];
                        Flags = $natFlags;
                    };
                }
            }

            if ($PAPolicy) {
                $endpoint.Policies += @{
                    Type = "ProviderAddress";
                    Settings = @{
                        ProviderAddress = $PAPolicy["PA"];
                    }
                }
            }

            # Try to Generate the data
            $EndpointData = convertto-json $endpoint -Depth 10
        }

    }
    Process
    {
        $id = [Guid]::Empty
        $settings = $EndpointData
        $handle = 0
        $result = ""
        $networkHandle = 0
        $hnsClientApi = Get-HnsClientNativeMethods
        $hr = $hnsClientApi::HcnOpenNetwork($NetworkId, [ref] $networkHandle, [ref] $result);
        ReportErrors -FunctionName HcnOpenNetwork -Hr $hr -Result $result -ThrowOnFail
        $result = ""
        $hr = $hnsClientApi::HcnCreateEndpoint($networkHandle, $id, $settings, [ref] $handle, [ref] $result);
        ReportErrors -FunctionName HcnCreateEndpoint -Hr $hr -Result $result -ThrowOnFail

        $query =  '{"SchemaVersion": { "Major": 1, "Minor": 0 }}'
        $properties = "";
        $result = ""
        $hr = $hnsClientApi::HcnQueryEndpointProperties($handle, $query, [ref] $properties, [ref] $result);
        ReportErrors -FunctionName HcnQueryEndpointProperties -Hr $hr -Result $result
        $hr = $hnsClientApi::HcnCloseEndpoint($handle);
        ReportErrors -FunctionName HcnCloseEndpoint -Hr $hr
        $hr = $hnsClientApi::HcnCloseNetwork($networkHandle);
        ReportErrors -FunctionName HcnCloseNetwork -Hr $hr

        return ConvertResponseFromJson -JsonInput $properties
    }
}

function New-HnsRemoteEndpoint
{
    param
    (
        [parameter(Mandatory = $true)] [Guid] $NetworkId,
        [parameter(Mandatory = $false)] [string] $IPAddress,
        [parameter(Mandatory = $false)] [string] $MacAddress,
        [parameter(Mandatory = $false)] [string] $DNSServerList
    )

    return New-HnsEndpoint -NetworkId $NetworkId -IPAddress $IPAddress -MacAddress $MacAddress -DNSServerList $DNSServerList -RemoteEndpoint
}
function Get-HnsEndpoint
{
    param
    (
        [parameter(Mandatory=$false)] [Guid] $Id = [Guid]::Empty,
        [parameter(Mandatory=$false)] [Guid] $NetworkId = [Guid]::Empty,
        [parameter(Mandatory=$false)] [string] $NetworkName = "",
        [parameter(Mandatory=$false)] [int] $Version,
        [parameter(Mandatory=$false)] [switch] $Detailed

    )

    $Filter = @{}
    if(-NOT [String]::IsNullOrEmpty($NetworkName))
    {
        $Filter += @{
            VirtualNetworkName = $NetworkName;
        }
    }
    if($NetworkId -NE [Guid]::Empty)
    {
        $Filter += @{
            VirtualNetwork = $NetworkId;
        }
    }
    if($Detailed.IsPresent)
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $EndpointNativeMethods -Filter $Filter -Version $Version -Detailed
    }
    else
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $EndpointNativeMethods -Filter $Filter -Version $Version
    }   
}

function Modify-HnsEndpoint
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$true)] [ModifyRequestType] $RequestType,
        [parameter(Mandatory=$false)] [EndpointResourceType] $ResourceType,
        [HashTable][parameter(Mandatory=$false)] $Settings,
        [HashTable[]][parameter(Mandatory=$false)] $PolicyArray
    )

    $msettings = @{
        RequestType = "$RequestType";
        ResourceType = "$ResourceType";
    }

    if ($Settings)
    {
        $msettings += @{
            Settings = $Settings;
        }
    }
    elseif($PolicyArray)
    {
        $policies = @{
            Policies = $PolicyArray;
        }
        $msettings += @{
            Settings = $policies;
        }
    }

    return Modify-HnsGeneric -Id $Id -NativeMethods $EndpointNativeMethods -Settings $msettings
}



function Add-HnsEndpointVmPort
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$false)] [Guid]$PortId,
        [parameter(Mandatory=$false)] [Guid]$VirtualMachineId
    )

    $settings = @{
        PortId = $PortId;
        VirtualMachineId = $VirtualMachineId;
        VirtualNicName = "$VirtualMachineId--$Id"
    }
    $requestType = [ModifyRequestType]::Add
    $resourceType = [EndpointResourceType]::Port
    Modify-HnsEndpoint -Id $Id -RequestType $requestType -ResourceType $resourceType -Settings $settings
}

function Remove-HnsEndpointVmPort
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id
    )

    $settings = @{}
    $requestType = [ModifyRequestType]::Remove
    $resourceType = [EndpointResourceType]::Port
    Modify-HnsEndpoint -Id $Id -RequestType $requestType -ResourceType $resourceType -Settings $settings
}

function Remove-HnsEndpoint
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True)]
        [Object[]] $InputObjects
    )
    begin {$objects = @()}
    process {$Objects += $InputObjects;}
    end {
        Remove-HnsGeneric -InputObjects $Objects -NativeMethods $EndpointNativeMethods
    }
}
function Add-HnsEndpointPolicy {
    param
    (
        [parameter(Mandatory = $false)] [Guid[]] $Endpoints = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $Policies 
    )
    $requestType = [ModifyRequestType]::Add
    $resourceType = [EndpointResourceType]::Policy
    foreach ($id in $Endpoints) {
        $ep = Get-HnsEndpoint -Id $id -Version 2
        Modify-HnsEndpoint -Id $Id -RequestType $requestType -ResourceType $resourceType -PolicyArray $Policies
    }
}
function Remove-HnsEndpointPolicy {
    param
    (
        [parameter(Mandatory = $false)] [Guid[]] $Endpoints = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $Policies 
    )
    $requestType = [ModifyRequestType]::Remove
    $resourceType = [EndpointResourceType]::Policy
    foreach ($id in $Endpoints) {
        $ep = Get-HnsEndpoint -Id $id -Version 2
        Modify-HnsEndpoint -Id $Id -RequestType $requestType -ResourceType $resourceType -PolicyArray $Policies
    }
}
function Update-HnsEndpointPolicy {
    param
    (
        [parameter(Mandatory = $false)] [Guid[]] $Endpoints = $null,
        [parameter(Mandatory = $false)] [HashTable[]] $Policies 
    )
    $requestType = [ModifyRequestType]::Update
    $resourceType = [EndpointResourceType]::Policy
    foreach ($id in $Endpoints) {
        $ep = Get-HnsEndpoint -Id $id -Version 2
        Modify-HnsEndpoint -Id $Id -RequestType $requestType -ResourceType $resourceType -PolicyArray $Policies
    }
}

function New-HnsProxyPolicy {
    param
    (
        [parameter(Mandatory = $false)] [Guid[]] $Endpoints = $null,
        [parameter(Mandatory = $false)] [string] $DestinationPrefix,
        [parameter(Mandatory = $false)] [string] $DestinationPort,
        [parameter(Mandatory = $false)] [string] $Destination,
        [parameter(Mandatory = $false)] [string[]] $ExceptionList,
        [parameter(Mandatory = $false)] [bool] $OutboundNat
    )
    $ProxyPolicy = @{}
    $Type = "L4Proxy";
        
    if ($DestinationPrefix) {
        $ProxyPolicy['IP'] = $DestinationPrefix
    }
    if ($DestinationPort) {
        $ProxyPolicy['Port'] = $DestinationPort
    }
    if ($ExceptionList) {
        $ProxyPolicy['ExceptionList'] = $ExceptionList
    }
    if ($Destination) {
        $ProxyPolicy['Destination'] = $Destination
    }
    if ($OutboundNat) {
        $ProxyPolicy['OutboundNat'] = $OutboundNat
    }
    
    $Settings   = @{
            Type = $type;
            Settings = $ProxyPolicy;
    };
    Update-HnsEndpointPolicy -Endpoints $Endpoints -Policies @($Settings)
    
}

function Remove-HnsProxyPolicy {
    param
    (
        [parameter(Mandatory = $false)] [Guid[]] $Endpoints = $null
    )
    
    Update-HnsEndpointPolicy -Endpoints $Endpoints -Policies @(@{})
}




################################
# GuestNetworkService
################################
Add-Type -TypeDefinition @"
    public enum GuestNetworkServiceResourceType
    {
        State,
    };
 
    public enum GuestNetworkServiceState
    {
        None,
        Created,
        Bootstrapping,
        Synchronized,
        Paused,
        Desynchronized,
        Rehydrating,
        Degraded,
        Destroyed,
    };
"@


function Get-HnsGuestNetworkService
{
    param
    (
        [parameter(Mandatory=$false)] [Guid] $Id = [Guid]::Empty,
        [parameter(Mandatory=$false)] [switch] $Detailed,
        [parameter(Mandatory=$false)] [int] $Version
    )
    if ($Detailed.IsPresent)
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $GuestNetworkServiceNativeMethods -Version $Version -Detailed
    }
    else
    {
        return Get-HnsGeneric -Id $Id -NativeMethods $GuestNetworkServiceNativeMethods -Version $Version
    }
}

function Modify-HnsGuestNetworkService
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$true)] [ModifyRequestType] $RequestType,
        [parameter(Mandatory=$false)] [GuestNetworkServiceResourceType] $ResourceType,
        [HashTable][parameter(Mandatory=$false)] $Settings
    )
    $msettings = @{
        RequestType = "$RequestType";
        ResourceType = "$ResourceType";
    }

    if ($Settings)
    {
        $msettings += @{
            Settings = $Settings;
        }
    }

    return Modify-HnsGeneric -Id $Id -NativeMethods $GuestNetworkServiceNativeMethods -Settings $msettings
}

function Modify-HnsGuestNetworkServiceState
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $Id,
        [parameter(Mandatory=$true)] [GuestNetworkServiceState] $State
    )
    $requestType = [ModifyRequestType]::Update
    $resourceType = [GuestNetworkServiceResourceType]::State
    Modify-HnsGuestNetworkService -Id $Id -RequestType $requestType -ResourceType $resourceType -Settings @{ State = "$State"}
}

function ReportErrors
{
    param
    (
        [parameter(Mandatory=$false)]
        [string] $FunctionName,
        [parameter(Mandatory=$true)]
        [Int64] $Hr,
        [parameter(Mandatory=$false)]
        [string] $Result,
        [switch] $ThrowOnFail
    )

    $errorOutput = ""

    if($Hr -ne 0)
    {
        $errorOutput += "HRESULT: $($Hr). "
    }

    if(-NOT [string]::IsNullOrWhiteSpace($Result))
    {
        $errorOutput += "Result: $($Result)"
    }

    if(-NOT [string]::IsNullOrWhiteSpace($errorOutput))
    {
        $errString = "$($FunctionName) -- $($errorOutput)"
        if($ThrowOnFail.IsPresent)
        {
            throw $errString
        }
        else {
            Write-Error $errString
        }
    }
}

function ConvertResponseFromJson
{
    param
    (
        [parameter(Mandatory=$false)]
        [string] $JsonInput
    )

    $output = "";
    if ($JsonInput)
    {
        try {
            $output = ($JsonInput | ConvertFrom-Json);
        } catch {
            Write-Error $_.Exception.Message
            return ""
        }
        if ($output.Error)
        {
             Write-Error $output;
        }
    }

    return $output;
}

#########################################################################


#### COPIED FROM V1 TO USE FOR KUBERNETES TESTS ####

#########################################################################

function Get-VmComputeNativeMethods()
{
        $signature = @'
                     [DllImport("vmcompute.dll")]
                     public static extern void HNSCall([MarshalAs(UnmanagedType.LPWStr)] string method, [MarshalAs(UnmanagedType.LPWStr)] string path, [MarshalAs(UnmanagedType.LPWStr)] string request, [MarshalAs(UnmanagedType.LPWStr)] out string response);
'@


    # Compile into runtime type
    Add-Type -MemberDefinition $signature -Namespace VmCompute.HNSPrivate.PrivatePInvoke -Name NativeMethods -PassThru
}


function Attach-HnsHostEndpoint
{
    param
    (
     [parameter(Mandatory=$true)] [Guid] $EndpointID,
     [parameter(Mandatory=$true)] [int] $CompartmentID
     )
    $request = @{
        SystemType    = "Host";
        CompartmentId = $CompartmentID;
    };

    return Invoke-HnsEndpointRequest -Method POST -Data (ConvertTo-Json $request -Depth 10) -Action attach -Id $EndpointID
}

function Attach-HnsVMEndpoint
{
    param
    (
     [parameter(Mandatory=$true)] [Guid] $EndpointID,
     [parameter(Mandatory=$true)] [string] $VMNetworkAdapterName
     )

    $request = @{
        VirtualNicName   = $VMNetworkAdapterName;
        SystemType    = "VirtualMachine";
    };
    return Invoke-HnsEndpointRequest -Method POST -Data (ConvertTo-Json $request -Depth 10) -Action attach -Id $EndpointID

}

function Attach-HnsEndpoint
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $EndpointID,
        [parameter(Mandatory=$true)] [int] $CompartmentID,
        [parameter(Mandatory=$true)] [string] $ContainerID
    )
     $request = @{
        ContainerId = $ContainerID;
        SystemType="Container";
        CompartmentId = $CompartmentID;
    };

    return Invoke-HnsEndpointRequest -Method POST -Data (ConvertTo-Json $request -Depth 10) -Action attach -Id $EndpointID
}

function Detach-HnsVMEndpoint
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $EndpointID
    )
    $request = @{
        SystemType  = "VirtualMachine";
    };

    return Invoke-HnsEndpointRequest -Method POST -Data (ConvertTo-Json $request -Depth 10) -Action detach -Id $EndpointID
}

function Detach-HnsHostEndpoint
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $EndpointID
    )
    $request = @{
        SystemType  = "Host";
    };

    return Invoke-HnsEndpointRequest -Method POST -Data (ConvertTo-Json $request -Depth 10) -Action detach -Id $EndpointID
}

function Detach-HnsEndpoint
{
    param
    (
        [parameter(Mandatory=$true)] [Guid] $EndpointID,
        [parameter(Mandatory=$true)] [string] $ContainerID
    )

    $request = @{
        ContainerId = $ContainerID;
        SystemType="Container";
    };

    return Invoke-HnsEndpointRequest -Method POST -Data (ConvertTo-Json $request -Depth 10) -Action detach -Id $EndpointID
}

#########################################################################

function Invoke-HnsEndpointRequest
{
    param
    (
        [ValidateSet('GET', 'POST', 'DELETE')]
        [parameter(Mandatory=$true)] [string] $Method,
        [ValidateSet('attach', 'detach', 'detailed')]
        [parameter(Mandatory=$false)] [string] $Action = $null,
        [parameter(Mandatory=$false)] [string] $Data = $null,
        [parameter(Mandatory=$false)] [string] $Id = $null
    )
    return Invoke-HnsRequest -Method $Method -Type endpoints -Action $Action -Data $Data -Id $Id
}

#########################################################################

function Invoke-HnsRequest
{
    param
    (
        [ValidateSet('GET', 'POST', 'DELETE')]
        [parameter(Mandatory=$true)] [string] $Method,
        [ValidateSet('networks', 'endpoints', 'activities', 'policylists', 'endpointstats', 'plugins', 'namespaces', 'globals')]
        [parameter(Mandatory=$true)] [string] $Type,
        [parameter(Mandatory=$false)] [string] $Action = $null,
        [parameter(Mandatory=$false)] [string] $Data = $null,
        [parameter(Mandatory=$false)] [string] $Id = $null
    )

    $hnsPath = "/$Type"

    if ($id)
    {
        $hnsPath += "/$id";
    }

    if ($Action)
    {
        $hnsPath += "/$Action";
    }

    $request = "";
    if ($Data)
    {
        $request = $Data
    }

    $output = "";
    $response = "";
    Write-Verbose "Invoke-HnsRequest Type[$Type] Method[$Method] Path[$hnsPath] Data[$request]"

    $hnsApi = Get-VmComputeNativeMethods
    $hnsApi::HNSCall($Method, $hnsPath, "$request", [ref] $response);

    Write-Verbose "Result : $response"
    if ($response)
    {
        try {
            $output = ($response | ConvertFrom-Json);
        } catch {
            Write-Error $_.Exception.Message
            return ""
        }
        if ($output.Error)
        {
            Write-Error $output;
        }
        $output = $output.Output;
    }

    return $output;
}