├── .build ├── build.ps1 ├── clean.ps1 ├── generate-documentation.ps1 ├── generate-module-manifest.ps1 ├── generate-version.ps1 ├── package.ps1 └── restore.ps1 ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── contributing.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── build-pipeline.yml │ ├── dependency-review.yml │ ├── powershell.yml │ ├── scorecards.yml │ ├── server2019-sdntest-pr.yml │ └── server2019-sdntest.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── SdnDiagnostics.nuspec ├── build.ps1 ├── src ├── SdnDiagnostics.psd1 ├── SdnDiagnostics.psm1 └── modules │ ├── SdnDiag.Common.Config.psd1 │ ├── SdnDiag.Common.psm1 │ ├── SdnDiag.Gateway.Config.psd1 │ ├── SdnDiag.Gateway.psm1 │ ├── SdnDiag.Health.Config.psd1 │ ├── SdnDiag.Health.psm1 │ ├── SdnDiag.LoadBalancerMux.Config.psd1 │ ├── SdnDiag.LoadBalancerMux.psm1 │ ├── SdnDiag.NetworkController.Config.psd1 │ ├── SdnDiag.NetworkController.FC.Config.psd1 │ ├── SdnDiag.NetworkController.FC.psm1 │ ├── SdnDiag.NetworkController.SF.Config.psd1 │ ├── SdnDiag.NetworkController.SF.psm1 │ ├── SdnDiag.NetworkController.psm1 │ ├── SdnDiag.Server.Config.psd1 │ ├── SdnDiag.Server.psm1 │ ├── SdnDiag.Utilities.Config.psd1 │ ├── SdnDiag.Utilities.psm1 │ └── Test-SdnExpressBgp.psm1 └── tests ├── README.md ├── offline ├── NetworkController.Tests.ps1 ├── RunTests.ps1 ├── SoftwareLoadBalancer.Tests.ps1 └── data │ ├── Get-SdnInfrastructureInfo.xml │ └── SdnApiResources │ ├── accessControlLists.json │ ├── credentials.json │ ├── gatewayPools.json │ ├── gateways.json │ ├── iDNSServer_configuration.json │ ├── loadBalancerManager_config.json │ ├── loadBalancerMuxes.json │ ├── loadBalancers.json │ ├── logicalNetworks.json │ ├── macPools.json │ ├── networkInterfaces.json │ ├── publicIPAddresses.json │ ├── routeTables.json │ ├── servers.json │ ├── virtualGateways.json │ ├── virtualNetworkManager_configuration.json │ ├── virtualNetworks.json │ └── virtualServers.json └── online ├── RunTests.ps1 ├── SdnDiagnosticsTestConfig-Sample.psd1 ├── wave1 └── Utilities.Tests.ps1 └── waveAll ├── Debug-SdnFabricInfrastructure.Tests.ps1 ├── Get-SdnInfrastructureInfo.Tests.ps1 └── Start-SdnDataCollection.Tests.ps1 /.build/build.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [System.IO.DirectoryInfo]$OutputDirectory = "$PSScriptRoot\..\out\build" 3 | ) 4 | 5 | $currentErrorPref = $ErrorActionPreference 6 | $ErrorActionPreference = 'Stop' 7 | 8 | if (-NOT (Test-Path -Path $OutputDirectory.FullName -PathType Container)) { 9 | $null = New-Item -ItemType:Directory -Path $OutputDirectory.FullName -Force 10 | } 11 | 12 | $outDirFolder = New-Item -Path (Join-Path -Path $OutputDirectory.FullName -ChildPath "SdnDiagnostics\") -ItemType Directory -Force 13 | Copy-Item -Path "$PSScriptRoot\..\src\*" -Destination $outDirFolder.FullName -Recurse 14 | 15 | "Successfully generated the directory module structure under $($OutputDirectory.FullName)" | Write-Host 16 | $ErrorActionPreference = $currentErrorPref 17 | -------------------------------------------------------------------------------- /.build/clean.ps1: -------------------------------------------------------------------------------- 1 | $outDir = Get-Item -Path "$PSScriptRoot\..\out" -ErrorAction SilentlyContinue 2 | if ($outDir) { 3 | "Removing all items under {0}" -f $outDir.FullName | Write-Host 4 | Remove-Item -Path "$($outDir.FullName)\*" -Recurse -Force 5 | } 6 | -------------------------------------------------------------------------------- /.build/generate-documentation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Builds the markdown documentation for the module. 4 | .DESCRIPTION 5 | Builds the markdown documentation for the module using the PlatyPS PowerShell module. 6 | #> 7 | 8 | $ErrorActionPreference = "Stop" 9 | 10 | $platyFromPoshGallery = Find-Module -Name platyPS 11 | $platyFromLocal = Get-Module -ListAvailable -Name platyPS | Sort-Object Version -Descending | Select-Object -First 1 12 | 13 | if($null -ne $platyFromLocal) { 14 | if([Version]$platyFromPoshGallery.Version -gt [Version]$platyFromLocal.Version){ 15 | Install-Module -Name platyPS -Scope CurrentUser -Confirm:$false -Force 16 | } 17 | } 18 | else { 19 | Install-Module -Name platyPS -Scope CurrentUser -Confirm:$false -Force 20 | } 21 | 22 | $modulePath = "$PSScriptRoot\..\src\SdnDiagnostics.psd1" 23 | $docPath = "$PSScriptRoot\..\.documentation\functions" 24 | $sideBarNav = "$PSScriptRoot\..\.documentation\_SideBar.md" 25 | 26 | if(-NOT (Test-Path -Path $docPath -PathType Container)) { 27 | $null = New-Item -Path $docPath -ItemType Directory -Force 28 | } 29 | 30 | Import-Module -Name platyPS -Force 31 | Import-Module -Name $modulePath -Force 32 | 33 | # remove existing articles as this helps ensure any deprecated exported function does not get published 34 | $oldArticles = Get-ChildItem -Path "$docPath\*" -Include *.md 35 | if($oldArticles){ 36 | "Removing existing documentation to ensure clean build" | Write-Host 37 | $oldArticles | Remove-Item -Force 38 | } 39 | 40 | # generate the latest markdown files 41 | "Generating function documentation" | Write-Host 42 | $null = New-MarkdownHelp -Module SdnDiagnostics -OutputFolder $docPath -NoMetadata -Force 43 | 44 | $currentFiles = Get-ChildItem -Path $docPath\* -Include *.md 45 | foreach($function in (Get-Command -Module SdnDiagnostics)){ 46 | if($function.Name -inotin ($currentFiles).BaseName){ 47 | "Documentation not generated for {0}" -f $function.Name | Write-Host -ForegroundColor:Yellow 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.build/generate-module-manifest.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [System.IO.FileInfo]$Manifest, 3 | [string]$Version 4 | ) 5 | 6 | $currentErrorPref = $ErrorActionPreference 7 | $ErrorActionPreference = 'Stop' 8 | 9 | if ([String]::IsNullOrEmpty($Version)) { 10 | [String]$Version = $env:CUSTOM_VERSION 11 | } 12 | 13 | try { 14 | $manifestData = Import-PowerShellDataFile -Path $Manifest.FullName -Verbose 15 | $modParams = @{ 16 | RootModule = $manifestData.RootModule 17 | Author = $manifestData.Author 18 | CompanyName = $manifestData.CompanyName 19 | Copyright = $manifestData.Copyright 20 | GUID = $manifestData.GUID 21 | Description = $manifestData.Description 22 | ModuleVersion = $Version 23 | PowershellVersion = $manifestData.PowerShellVersion 24 | NestedModules = $manifestData.NestedModules 25 | RequiredModules = $manifestData.RequiredModules 26 | CmdletsToExport = $manifestData.CmdletsToExport 27 | FunctionsToExport = $manifestData.FunctionsToExport 28 | VariablesToExport = $manifestData.VariablesToExport 29 | AliasesToExport = $manifestData.AliasesToExport 30 | Tags = $manifestData.PrivateData.PSData.Tags 31 | LicenseUri = $manifestData.PrivateData.PSData.LicenseUri 32 | ProjectUri = $manifestData.PrivateData.PSData.ProjectUri 33 | } 34 | 35 | Remove-Item -Path $Manifest.FullName -Force -Verbose 36 | New-ModuleManifest -Path $Manifest.FullName @modParams -Verbose 37 | } 38 | catch { 39 | "Failed to update the generate manifest for $($Manifest.BaseName)`n{0}" -f $_.Exception | Write-Error 40 | exit 1 41 | } 42 | 43 | $ErrorActionPreference = $currentErrorPref 44 | -------------------------------------------------------------------------------- /.build/generate-version.ps1: -------------------------------------------------------------------------------- 1 | $major = [int]4 # Major version number 2 | $minor = [DateTime]::UtcNow.Year.ToString().Substring(2,2) + [DateTime]::UtcNow.Month.ToString().PadLeft(2,"0") # 2002, 2104, etc for current month 3 | 4 | # we want to generate the patch number based on the current day 5 | # this will return int32 value so should be between 1 and 31 6 | $patch = [DateTime]::UtcNow.Day 7 | 8 | # we want to generate the revision number based on the current hour and minute 9 | # this can be between 2-4 digit number depending on the hour and minute 10 | # we want to make sure that the hour is not 0 and does not have a leading zero 11 | [int]$hour = [DateTime]::UtcNow.Hour 12 | if ($hour -ieq 0) {$hour++} 13 | [int]$minute = [DateTime]::UtcNow.Minute.ToString().PadLeft(2,"0") 14 | if ($minute -ieq 0) {$minute++} 15 | $revision = "{0}{1}" -f $hour.ToString().Trim(), $minute.ToString().Trim() 16 | 17 | # we now have our build number 18 | # we want to format as #.YYMM.DD.HHMM : 8.2104.12.1120 19 | $buildNumber = "{0}.{1}.{2}.{3}" -f $major, $minor, $patch.ToString().Trim(), $revision.Trim() 20 | 21 | [Environment]::SetEnvironmentVariable("CUSTOM_VERSION", $buildNumber, "User") 22 | Write-Host "##vso[task.setvariable variable=CUSTOM_VERSION;]${buildNumber}" 23 | 24 | return $buildNumber 25 | -------------------------------------------------------------------------------- /.build/package.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [String]$Version 3 | ) 4 | 5 | $nugetSpec = Get-Item -Path "$PSScriptRoot\..\Sdndiagnostics.nuspec" | Select-Object -ExpandProperty FullName 6 | $rootDir = (Resolve-Path -LiteralPath "$PSScriptRoot\..\").Path 7 | $outputPackagePath = Join-Path -Path $rootDir -ChildPath "out\packages" 8 | 9 | if (Test-Path $outputPackagePath -PathType Container) { 10 | Write-Host "Removing old packages from package folder $outputPackagePath" 11 | Remove-Item -Path $outputPackagePath -Recurse -Force 12 | } 13 | 14 | $null = New-Item $outputPackagePath -Type Directory -Force 15 | 16 | Write-Host "`nBuilding Nuget Package:" 17 | Write-Host "`tSpec file path: $($nugetSpec)" 18 | Write-Host "`tOutput directory: $($outputPackagePath)" 19 | Write-Host "`tVersion: $($Version)`n" 20 | 21 | nuget pack $nugetSpec -OutputDirectory $outputPackagePath -properties "version=$Version;rootDir=$rootDir" -NoPackageAnalysis 22 | -------------------------------------------------------------------------------- /.build/restore.ps1: -------------------------------------------------------------------------------- 1 | "Restoring nuget packages for SdnDiagnostics" | Write-Host 2 | 3 | $pkgConfig = "$PSScriptRoot\..\packages.config" 4 | $rstrPath = "$PSScriptRoot\..\.packages" 5 | 6 | if (!(Test-Path -Path $rstrPath -PathType Container)) { 7 | $null = New-Item -Path $rstrPath -ItemType Directory -Force 8 | } 9 | else { 10 | Remove-Item -Path $rstrPath\* -Recurse -Force 11 | } 12 | 13 | # Put all redistributed and github repos into this directory. 14 | nuget restore $pkgConfig -OutputDirectory $rstrPath 15 | 16 | # Check exit code and exit with non-zero exit code so that build will fail. 17 | if ($LASTEXITCODE -ne 0){ 18 | "Failed to restore packages correctly." | Write-Error 19 | exit $LASTEXITCODE 20 | } 21 | 22 | exit $LASTEXITCODE 23 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo. Unless a later match takes precedence, 2 | # @sdndiagnostics will be requested for review when someone opens a pull request. 3 | * @microsoft/sdndiagnostics 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment Details (please complete the following information):** 27 | - Windows Server OS: 28 | - SdnDiagnostics Version: 29 | 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Welcome to SdnDiagnostics contributing guide 2 | When contributing to this project, ensure you: 3 | 1. Review existing functions already available and reuse where possible. 4 | 1. Return native .NET objects whenever possible. 5 | 1. Rarely should you return from a function with format-table, format-list, etc.. If they do, they should use the PowerShell verb `Show`. 6 | 1. Environments that this module run on may be in a broken or inconcistent state, so defensive coding techniques should be leveraged. 7 | 1. Use [PowerShell Approved Verbs](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands) when creating functions. 8 | 1. Provide detailed function synopsis, description, parameters and examples. The build pipeline leverages platyPS to auto-generate documentation for the exported functions and publishes to the project wiki. 9 | 10 | # Getting started 11 | 12 | ## Creating functions 13 | When creating functions: 14 | 15 | 1. Functions should be placed under the respective module that they execute under based on the role. 16 | 1. If your function should be exported and available after module import, be sure to add your function to the export list in `src\SdnDiagnostics.psd1` under `FunctionsToExport`. 17 | 18 | # Build validation and testing 19 | 1. To generate a local build of the module, run `.\build.ps1` which will generate an SdnDiagnostics module package to `~\out\build\SdnDiagnostics`. 20 | 1. Copy the module to `C:\Program Files\WindowsPowerShell\Modules`. 21 | - Remove any existing modules if they are present. 22 | - Folder path structure should resemble `\SdnDiagnostics\{version}` 23 | 1. Import the module using `Import-Module -Name SdnDiagnostics -Force`. 24 | - If you have already imported the `SdnDiagnostics` module and need to re-import, you first must `Remove-Module -Name SdnDiagnostics` and then perform the `Import-Module` operation. This will ensure that the nested modules, classes and enums are loaded correctly. 25 | 1. Install the modules to the SDN nodes in the dataplane. 26 | ```powershell 27 | $environmentDetails = Get-SdnInfrastructureInfo -NetworkController 'NC01' 28 | Install-SdnDiagnostics -ComputerName $environmentDetails.FabricNodes 29 | ``` 30 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | 8 | - package-ecosystem: nuget 9 | directory: / 10 | schedule: 11 | interval: daily 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Summary of changes: 3 | - changes 4 | 5 | # Change type 6 | - [ ] Bug fix (non-breaking change) 7 | - [ ] Code style update (formatting, local variables) 8 | - [ ] New Feature (non-breaking change that adds new functionality without impacting existing) 9 | - [ ] Breaking change (fix or feature that may cause functionality impact) 10 | - [ ] Other 11 | 12 | # Checklist: 13 | - [ ] My code follows the style and contribution guidelines of this project. 14 | - [ ] I have tested and validated my code changes. -------------------------------------------------------------------------------- /.github/workflows/build-pipeline.yml: -------------------------------------------------------------------------------- 1 | name: Build Pipeline 2 | 3 | # Controls when the workflow will run 4 | on: 5 | # Triggers the workflow on push events but only for the main branch 6 | push: 7 | branches: 8 | - main 9 | paths: 10 | - 'src/**' 11 | 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | permissions: 18 | contents: read 19 | 20 | jobs: 21 | # This workflow contains a single job called "build" 22 | build: 23 | # The type of runner that the job will run on 24 | runs-on: windows-latest 25 | 26 | # Steps represent a sequence of tasks that will be executed as part of the job 27 | steps: 28 | - name: Harden Runner 29 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 30 | with: 31 | egress-policy: audit 32 | 33 | - name: 'Checkout SdnDiagnostics' 34 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 35 | with: 36 | path: main 37 | 38 | - name: 'Build SdnDiagnostics Module and Nuget Package' 39 | run: | 40 | Set-Location -Path .\main 41 | & .\build.ps1 42 | shell: powershell 43 | 44 | - name: 'Publish to Nuget Gallery' 45 | run: | 46 | nuget.exe push ".\main\out\packages\SdnDiagnostics.*.nupkg" -ApiKey ${{ secrets.NUGET_AUTH_TOKEN }} -Source https://api.nuget.org/v3/index.json 47 | shell: powershell 48 | 49 | - name: 'Publish to PowerShell Gallery' 50 | run: | 51 | $nuGet = Get-Item -Path ".\main\out\packages\SdnDiagnostics*.nupkg" 52 | $zipFolder = Rename-Item -Path $nuGet.FullName -NewName "SdnDiagnostics.zip" -Force -PassThru 53 | Expand-Archive -Path $zipFolder.FullName -Destination (Split-Path -Path $zipFolder.FullName -Parent) 54 | Publish-Module -Path "$(Split-Path -Path $zipFolder.FullName -Parent)\SdnDiagnostics" -NuGetApiKey ${{ secrets.PSGALLERY_AUTH_TOKEN }} -SkipAutomaticTags 55 | shell: powershell 56 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: 'Dependency Review' 10 | on: [pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 21 | with: 22 | egress-policy: audit 23 | 24 | - name: 'Checkout Repository' 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - name: 'Dependency Review' 27 | uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 28 | -------------------------------------------------------------------------------- /.github/workflows/powershell.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # 6 | # https://github.com/microsoft/action-psscriptanalyzer 7 | # For more information on PSScriptAnalyzer in general, see 8 | # https://github.com/PowerShell/PSScriptAnalyzer 9 | 10 | name: PSScriptAnalyzer 11 | 12 | on: 13 | push: 14 | branches: [ "main" ] 15 | pull_request: 16 | branches: [ "main" ] 17 | schedule: 18 | - cron: '31 12 * * 0' 19 | 20 | permissions: 21 | contents: read 22 | 23 | jobs: 24 | build: 25 | permissions: 26 | contents: read # for actions/checkout to fetch code 27 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 28 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 29 | name: PSScriptAnalyzer 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Harden Runner 33 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 34 | with: 35 | egress-policy: audit 36 | 37 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 38 | 39 | - name: Run PSScriptAnalyzer 40 | uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f 41 | with: 42 | # Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options. 43 | # The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules. 44 | path: .\ 45 | recurse: true 46 | # Include your own basic security rules. Removing this option will run all the rules 47 | includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText", "AvoidDefaultValueForMandatoryParameter", "AvoidOverwritingBuiltInCmdlets", "AvoidUsingComputerNameHardcoded", "AvoidUsingUsernameAndPasswordParams", "AvoidUsingConvertToSecureStringWithPlainText", "UseSingularNouns", "UseApprovedVerbs", "UseCorrectCasing"' 48 | output: results.sarif 49 | 50 | # Upload the SARIF file generated in the previous step 51 | - name: Upload SARIF results file 52 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 53 | with: 54 | sarif_file: results.sarif 55 | -------------------------------------------------------------------------------- /.github/workflows/scorecards.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecard supply-chain security 6 | on: 7 | # For Branch-Protection check. Only the default branch is supported. See 8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 9 | branch_protection_rule: 10 | # To guarantee Maintained check is occasionally updated. See 11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 12 | schedule: 13 | - cron: '20 7 * * 2' 14 | push: 15 | branches: ["main"] 16 | 17 | # Declare default permissions as read only. 18 | permissions: read-all 19 | 20 | jobs: 21 | analysis: 22 | name: Scorecard analysis 23 | runs-on: ubuntu-latest 24 | permissions: 25 | # Needed to upload the results to code-scanning dashboard. 26 | security-events: write 27 | # Needed to publish results and get a badge (see publish_results below). 28 | id-token: write 29 | contents: read 30 | actions: read 31 | 32 | steps: 33 | - name: Harden Runner 34 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 35 | with: 36 | egress-policy: audit 37 | 38 | - name: "Checkout code" 39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 40 | with: 41 | persist-credentials: false 42 | 43 | - name: "Run analysis" 44 | uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 45 | with: 46 | results_file: results.sarif 47 | results_format: sarif 48 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 49 | # - you want to enable the Branch-Protection check on a *public* repository, or 50 | # - you are installing Scorecards on a *private* repository 51 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 52 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 53 | 54 | # Public repositories: 55 | # - Publish results to OpenSSF REST API for easy access by consumers 56 | # - Allows the repository to include the Scorecard badge. 57 | # - See https://github.com/ossf/scorecard-action#publishing-results. 58 | # For private repositories: 59 | # - `publish_results` will always be set to `false`, regardless 60 | # of the value entered here. 61 | publish_results: true 62 | 63 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 64 | # format to the repository Actions tab. 65 | - name: "Upload artifact" 66 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 67 | with: 68 | name: SARIF file 69 | path: results.sarif 70 | retention-days: 5 71 | 72 | # Upload the results to GitHub's code scanning dashboard. 73 | - name: "Upload to code-scanning" 74 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 75 | with: 76 | sarif_file: results.sarif 77 | -------------------------------------------------------------------------------- /.github/workflows/server2019-sdntest-pr.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Server2019SDNPullRequest 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on pull request events but only for the main branch 8 | pull_request: 9 | branches: 10 | - main 11 | paths: 12 | - 'src/**' 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 18 | permissions: 19 | contents: read 20 | 21 | jobs: 22 | # This workflow contains a single job called "build" 23 | build-and-test: 24 | # The type of runner that the job will run on 25 | runs-on: [self-hosted,Windows,X64] 26 | 27 | # Steps represent a sequence of tasks that will be executed as part of the job 28 | steps: 29 | - name: Harden Runner 30 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 31 | with: 32 | egress-policy: audit 33 | 34 | - name: Cleanup existing files 35 | run: | 36 | Remove-Item -Path .\* -Recurse -Force -Verbose 37 | shell: powershell 38 | 39 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 40 | - name: Checkout SdnDiagnostics repo 41 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 42 | with: 43 | ref: ${{ github.event.pull_request.head.sha }} 44 | 45 | # Runs a single command using the runners shell 46 | - name: Build SdnDiagnostics Module and Nuget Package 47 | run: | 48 | nuget.exe update -self 49 | .\build.ps1 50 | shell: powershell 51 | 52 | # Run the test configuration file that is locally on the test environment 53 | - name: Run online validation tests 54 | run: .\tests\online\RunTests.ps1 -ConfigurationFile "..\SdnDiagnosticsTestConfig.psd1" 55 | shell: powershell 56 | -------------------------------------------------------------------------------- /.github/workflows/server2019-sdntest.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Server2019SDN 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push request events but only for the main branch 8 | push: 9 | branches: 10 | - main 11 | paths: 12 | - 'src/**' 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 18 | permissions: 19 | contents: read 20 | 21 | jobs: 22 | # This workflow contains a single job called "build" 23 | build-and-test: 24 | # The type of runner that the job will run on 25 | runs-on: [self-hosted,Windows,X64] 26 | 27 | # Steps represent a sequence of tasks that will be executed as part of the job 28 | steps: 29 | - name: Harden Runner 30 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 31 | with: 32 | egress-policy: audit 33 | 34 | - name: Cleanup existing files 35 | run: | 36 | Remove-Item -Path .\* -Recurse -Force -Verbose 37 | shell: powershell 38 | 39 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 40 | - name: Checkout SdnDiagnostics repo 41 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 42 | with: 43 | ref: main 44 | 45 | # Runs a single command using the runners shell 46 | - name: Build SdnDiagnostics module 47 | run: .\build.ps1 48 | shell: powershell 49 | 50 | # Run the test configuration file that is locally on the test environment 51 | - name: Run online validation tests 52 | run: .\tests\online\RunTests.ps1 -ConfigurationFile "..\SdnDiagnosticsTestConfig.psd1" 53 | shell: powershell 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | .documentation 3 | .packages 4 | .vscode 5 | .vs 6 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/gitleaks/gitleaks 3 | rev: v8.16.3 4 | hooks: 5 | - id: gitleaks 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.4.0 8 | hooks: 9 | - id: end-of-file-fixer 10 | - id: trailing-whitespace 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/microsoft/SdnDiagnostics/badge)](https://scorecard.dev/viewer/?uri=github.com/microsoft/SdnDiagnostics) ![](https://github.com/microsoft/SdnDiagnostics/actions/workflows/dependency-review.yml/badge.svg) ![](https://github.com/microsoft/SdnDiagnostics/actions/workflows/powershell.yml/badge.svg) 2 | 3 | # Project 4 | SdnDiagnostics is a PowerShell module that is designed to simplify the diagnostic troubleshooting and data collection process when troubleshooting issues related to [Microsoft Software Defined Network](https://docs.microsoft.com/en-us/windows-server/networking/sdn/software-defined-networking). 5 | 6 | Please refer to the [wiki](https://github.com/microsoft/SdnDiagnostics/wiki) on how to install and use SdnDiagnostics in your environment. 7 | 8 | ## Package Statistics 9 | | Package Gallery | Total Downloads | Current Version | 10 | | :-- | :-- | :-- | 11 | | PowerShell Gallery | [![downloads](https://img.shields.io/powershellgallery/dt/SdnDiagnostics.svg?label=Downloads)](https://www.powershellgallery.com/packages/SdnDiagnostics) | [![downloads](https://img.shields.io/powershellgallery/v/SdnDiagnostics.svg?label=Version)](https://www.powershellgallery.com/packages/SdnDiagnostics) | 12 | | NuGet | [![downloads](https://img.shields.io/nuget/dt/SdnDiagnostics.svg?label=Downloads)](https://www.nuget.org/packages/SdnDiagnostics) |[![downloads](https://img.shields.io/nuget/v/SdnDiagnostics.svg?label=Version)](https://www.nuget.org/packages/SdnDiagnostics) 13 | 14 | # Contributing 15 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 16 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 17 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 18 | 19 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 20 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 21 | provided by the bot. You will only need to do this once across all repos using our CLA. 22 | 23 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 24 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 25 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 26 | 27 | To get started on contributing to this module, refer to the [contributing](https://github.com/microsoft/SdnDiagnostics/blob/main/.github/contributing.md) guide on this project. 28 | # Trademarks 29 | 30 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 31 | trademarks or logos is subject to and must follow 32 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 33 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 34 | Any use of third-party trademarks or logos are subject to those third-party's policies. 35 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses [GitHub Issues](https://github.com/microsoft/SdnDiagnostics/issues) to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | For help and questions about using this project, please use [GitHub Discussions](https://github.com/microsoft/SdnDiagnostics/discussions). 10 | 11 | ## Microsoft Support Policy 12 | 13 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 14 | -------------------------------------------------------------------------------- /SdnDiagnostics.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SdnDiagnostics 5 | $version$ 6 | Adam Rudell, Luyao Feng 7 | Microsoft Corporation 8 | MIT 9 | false 10 | SdnDiagnostics is a tool used to simplify the data collection and diagnostics of Windows Software Defined Networking. 11 | © Microsoft Corporation. All rights reserved. 12 | https://github.com/microsoft/SdnDiagnostics 13 | en-US 14 | MSFTNet,Microsoft,Windows,Network,Networking,SDN,Diagnostics 15 | SdnDiagnostics\docs\README.md 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | if (Get-Module -Name 'SdnDiagnostics') { 2 | Remove-Module -Name 'SdnDiagnostics' -Force 3 | } 4 | 5 | .$PSScriptRoot\.build\clean.ps1 6 | $buildVersion = .$PSScriptRoot\.build\generate-Version.ps1 7 | if ($buildVersion) { 8 | .$PSScriptRoot\.build\build.ps1 -Version $buildVersion 9 | .$PSScriptRoot\.build\generate-module-manifest.ps1 -Manifest (Get-Item -Path ".\out\build\SdnDiagnostics\SdnDiagnostics.psd1") -Version $buildVersion 10 | .$PSScriptRoot\.build\package.ps1 -Version $buildVersion 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/SdnDiagnostics.psd1: -------------------------------------------------------------------------------- 1 | # Module manifest for diagnostics for Software Defined Networking. 2 | # 3 | # Copyright='© Microsoft Corporation. All rights reserved.' 4 | # Licensed under the MIT License. 5 | 6 | @{ 7 | # Script module or binary module file associated with this manifest. 8 | RootModule = 'SdnDiagnostics.psm1' 9 | 10 | # Author of this module 11 | Author = 'Adam Rudell, Luyao Feng' 12 | 13 | # Company or vendor of this module 14 | CompanyName = 'Microsoft Corporation' 15 | 16 | # Copyright statement for this module 17 | Copyright = '(c) Microsoft Corporation. All rights reserved.' 18 | 19 | # ID used to uniquely identify this module 20 | GUID = 'c6cd3002-c6b1-4798-b532-2f939f527599' 21 | 22 | # Description of the functionality provided by this module 23 | Description = 'SdnDiagnostics is a tool used to simplify the data collection and diagnostics of Windows Software Defined Networking.' 24 | 25 | # Version number of this module. 26 | ModuleVersion = '0.0.0.0' 27 | 28 | # Minimum version of the PowerShell engine required by this module 29 | PowerShellVersion = '5.1' 30 | 31 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 32 | NestedModules = @( 33 | 'modules\SdnDiag.Common.psm1' 34 | 'modules\SdnDiag.Gateway.psm1' 35 | 'modules\SdnDiag.Health.psm1' 36 | 'modules\SdnDiag.LoadBalancerMux.psm1' 37 | 'modules\SdnDiag.NetworkController.psm1' 38 | 'modules\SdnDiag.NetworkController.FC.psm1' 39 | 'modules\SdnDiag.NetworkController.SF.psm1' 40 | 'modules\SdnDiag.Server.psm1' 41 | 'modules\SdnDiag.Utilities.psm1' 42 | 'modules\Test-SdnExpressBgp.psm1' 43 | ) 44 | 45 | # Modules that must be imported into the global environment prior to importing this module 46 | RequiredModules = @() 47 | 48 | # Cmdlets to export from this module 49 | CmdletsToExport = @() 50 | 51 | # Functions to export from this module 52 | FunctionsToExport = @( 53 | 'Clear-SdnWorkingDirectory', 54 | 'Copy-SdnFileFromComputer', 55 | 'Copy-SdnFileToComputer', 56 | 'Confirm-SdnServiceFabricHealthy', 57 | 'Convert-SdnEtwTraceToTxt', 58 | 'Debug-SdnFabricInfrastructure', 59 | 'Debug-SdnGateway', 60 | 'Debug-SdnLoadBalancerMux', 61 | 'Debug-SdnNetworkController', 62 | 'Debug-SdnServer', 63 | 'Disable-SdnDiagTraceOutputLogging', 64 | 'Disable-SdnRasGatewayTracing', 65 | 'Disable-SdnServiceFabricNode', 66 | 'Enable-SdnDiagTraceOutputLogging', 67 | 'Enable-SdnRasGatewayTracing', 68 | 'Enable-SdnServiceFabricNode', 69 | 'Enable-SdnVipTrace', 70 | 'Get-SdnAuditLog', 71 | 'Get-SdnApiEndpoint' 72 | 'Get-SdnCertificate', 73 | 'Get-SdnConfigState', 74 | 'Get-SdnDiagnosticLogFile', 75 | 'Get-SdnDiagTraceOutputLogging', 76 | 'Get-SdnDiagTraceOutputFile', 77 | 'Get-SdnEventLog', 78 | 'Get-SdnFabricInfrastructureResult', 79 | 'Get-SdnGateway', 80 | 'Get-SdnInfrastructureInfo', 81 | 'Get-SdnInternalLoadBalancer', 82 | 'Get-SdnLogFile', 83 | 'Get-SdnModuleConfiguration', 84 | 'Get-SdnMuxCertificate', 85 | 'Get-SdnMuxDistributedRouterIP', 86 | 'Get-SdnMuxState', 87 | 'Get-SdnMuxStatefulVip', 88 | 'Get-SdnMuxStatelessVip', 89 | 'Get-SdnMuxStats', 90 | 'Get-SdnMuxVip', 91 | 'Get-SdnMuxVipConfig', 92 | 'Get-SdnLoadBalancerMux', 93 | 'Get-SdnNetAdapterEncapOverheadConfig', 94 | 'Get-SdnNetAdapterRdmaConfig', 95 | 'Get-SdnNetworkInterfaceOutboundPublicIPAddress', 96 | 'Get-SdnNetworkController', 97 | 'Get-SdnNetworkControllerClusterInfo', 98 | 'Get-SdnNetworkControllerNode', 99 | 'Get-SdnNetworkControllerNodeCertificate' 100 | 'Get-SdnNetworkControllerRestCertificate', 101 | 'Get-SdnNetworkControllerState', 102 | 'Get-SdnOvsdbAddressMapping', 103 | 'Get-SdnOvsdbFirewallRule', 104 | 'Get-SdnOvsdbGlobalTable', 105 | 'Get-SdnOvsdbPhysicalPort', 106 | 'Get-SdnOvsdbRouterTable', 107 | 'Get-SdnOvsdbUcastMacRemoteTable', 108 | 'Get-SdnProviderAddress', 109 | 'Get-SdnPublicIPPoolUsageSummary', 110 | 'Get-SdnResource', 111 | 'Get-SdnServer', 112 | 'Get-SdnServerCertificate', 113 | 'Get-SdnServiceFabricApplication', 114 | 'Get-SdnServiceFabricApplicationHealth', 115 | 'Get-SdnServiceFabricClusterConfig', 116 | 'Get-SdnServiceFabricClusterHealth', 117 | 'Get-SdnServiceFabricClusterManifest', 118 | 'Get-SdnServiceFabricNode', 119 | 'Get-SdnServiceFabricPartition', 120 | 'Get-SdnServiceFabricReplica', 121 | 'Get-SdnServiceFabricService', 122 | 'Get-SdnSlbStateInformation', 123 | 'Get-SdnVipConfig', 124 | 'Get-SdnVfpVmSwitchPort', 125 | 'Get-SdnVMNetworkAdapter', 126 | 'Get-SdnVMNetworkAdapterPortProfile', 127 | 'Get-SdnVMSwitch', 128 | 'Get-SdnVfpPortGroup', 129 | 'Get-SdnVfpPortLayer', 130 | 'Get-SdnVfpPortRule', 131 | 'Get-SdnVfpPortState', 132 | 'Get-SdnWorkingDirectory', 133 | 'Import-SdnCertificate', 134 | 'Install-SdnDiagnostics', 135 | 'Invoke-SdnCommand', 136 | 'Invoke-SdnGetNetView', 137 | 'Invoke-SdnResourceDump', 138 | 'Invoke-SdnServiceFabricCommand', 139 | 'Move-SdnServiceFabricReplica', 140 | 'New-SdnSelfSignedCertificate', 141 | 'New-SdnCertificateRotationConfig', 142 | 'New-SdnExpressBgpHost', 143 | 'New-SdnMuxCertificate', 144 | 'New-SdnNetworkControllerNodeCertificate', 145 | 'New-SdnNetworkControllerRestCertificate', 146 | 'New-SdnServerCertificate', 147 | 'Remove-SdnExpressBgpHost', 148 | 'Repair-SdnDiagnosticsScheduledTask', 149 | 'Restart-SdnServiceFabricClusterNodes', 150 | 'Set-SdnCertificateAcl', 151 | 'Set-SdnNetworkController', 152 | 'Set-SdnResource', 153 | 'Set-SdnServiceFabricClusterConfig', 154 | 'Set-SdnVMNetworkAdapterPortProfile', 155 | 'Show-SdnVfpPortConfig', 156 | 'Show-SdnVipState', 157 | 'Start-SdnCertificateRotation', 158 | 'Start-SdnDataCollection', 159 | 'Start-SdnEtwTraceCapture', 160 | 'Start-SdnMuxCertificateRotation', 161 | 'Start-SdnServerCertificateRotation', 162 | 'Start-SdnNetshTrace', 163 | 'Start-SdnHealthFault', 164 | 'Stop-SdnEtwTraceCapture', 165 | 'Stop-SdnNetshTrace', 166 | 'Test-SdnCertificateRotationConfig', 167 | 'Test-SdnClusterServiceState', 168 | 'Test-SdnDiagnosticsCleanupTaskEnabled', 169 | 'Test-SdnExpressBGP', 170 | 'Test-SdnHostAgentConnectionStateToApiService', 171 | 'Test-SdnEncapOverhead', 172 | 'Test-SdnProviderAddressConnectivity', 173 | 'Test-SdnProviderNetwork', 174 | 'Test-SdnMuxConnectionStateToRouter', 175 | 'Test-SdnMuxConnectionStateToSlbManager', 176 | 'Test-SdnNetworkControllerApiNameResolution', 177 | 'Test-SdnNetworkControllerNodeRestInterface', 178 | 'Test-SdnResourceConfigurationState', 179 | 'Test-SdnResourceProvisioningState', 180 | 'Test-SdnServiceFabricApplicationHealth', 181 | 'Test-SdnServiceFabricClusterHealth', 182 | 'Test-SdnServiceFabricNodeStatus', 183 | 'Test-SdnConfigurationState', 184 | 'Test-SdnNonSelfSignedCertificateInTrustedRootStore', 185 | 'Test-SdnClusterServiceState', 186 | 'Test-SdnServiceState', 187 | 'Test-SdnVfpEnabledVMSwitch', 188 | 'Test-SdnVfpEnabledVMSwitchMultiple', 189 | 'Test-SdnVfpPortTuple' 190 | ) 191 | 192 | # Variables to export from this module 193 | VariablesToExport = @() 194 | 195 | # Aliases to export from this module 196 | AliasesToExport = @( 197 | 'Get-SdnEnvironmentInfo' 198 | ) 199 | 200 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 201 | PrivateData = @{ 202 | PSData = @{ 203 | # Tags applied to this module. These help with module discovery in online galleries. 204 | Tags = @( 205 | 'MSFTNet','Microsoft','Windows','Network','Networking','SDN','Diagnostics' 206 | ) 207 | 208 | # A URL to the main website for this project. 209 | ProjectUri = 'https://github.com/microsoft/SdnDiagnostics' 210 | 211 | # A URL to the license for this module. 212 | LicenseUri = 'https://microsoft.mit-license.org/' 213 | 214 | # External dependent modules of this module 215 | ExternalModuleDependencies = @( 216 | 'CimCmdlets', 'DnsClient', 'Microsoft.PowerShell.Archive', 'NetSecurity', 'NetTCPIP', 'SmbShare' 217 | ) 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.Common.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Name = "Common" 6 | WindowsFeature = @() 7 | DefaultLogDirectory = "C:\Windows\Tracing\SDNDiagnostics" 8 | DefaultLogFolders = @( 9 | "CrashDumps", 10 | "NCApplicationCrashDumps", 11 | "NCApplicationLogs", 12 | "PerfCounters", 13 | "SdnDiagnostics", 14 | "Traces" 15 | ) 16 | LogFileTypes = @( 17 | "*.blg", 18 | "*.cab", 19 | "*.dmp" 20 | "*.etl", 21 | "*.json", 22 | "*.log", 23 | "*.trace" 24 | "*.zip" 25 | ) 26 | Properties = @{ 27 | EventLogProviders = @( 28 | "Application" 29 | "System" 30 | ) 31 | EtwTraceProviders = @{} 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.Gateway.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Name = "Gateway" 6 | WindowsFeature = @( 7 | "RemoteAccess" 8 | ) 9 | RequiredModules = @() 10 | Properties = @{ 11 | CommonPaths = @{ 12 | RasGatewayTraces = "C:\Windows\Tracing" 13 | } 14 | EventLogProviders = @( 15 | "Microsoft-Windows-RasAgileVpn*" 16 | "Microsoft-Windows-RemoteAccess*" 17 | "Microsoft-Windows-VPN*" 18 | ) 19 | RegKeyPaths = @( 20 | "HKLM:\Software\Microsoft\NetworkController" 21 | "HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent" 22 | ) 23 | Services = @{ 24 | RemoteAccess = @{ 25 | Properties = @{ 26 | DisplayName = "Routing and Remote Access" 27 | } 28 | } 29 | } 30 | EtwTraceProviders = @{ 31 | IKE = @{ 32 | isOptional = $false 33 | Providers = @( 34 | "{106b464d-8043-46b1-8cb8-e92a0cd7a560}" 35 | ) 36 | Level = 4 37 | Keywords = "0xFFFFFFFFFFFFFFFF" 38 | } 39 | Rasgateway = @{ 40 | isOptional = $false 41 | Providers = @( 42 | "{EB171376-3B90-4169-BD76-2FB821C4F6FB}" 43 | "{24989972-0967-4E21-A926-93854033638E}" 44 | "{F3F35A3B-6D33-4C32-BC81-21513D8BD708}" 45 | "{58ac8283-c536-5244-63c2-eb41247e4a10}" 46 | "{2E67FCF3-C48E-4B2D-A689-A91D07EDB910}" 47 | "{9FD2B528-8D3D-42D0-8FDF-5B1998004278}" 48 | ) 49 | Level = 4 50 | Keywords = "0xFFFFFFFFFFFFFFFF" 51 | } 52 | } 53 | } 54 | ResourceName = "Gateways" 55 | } 56 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.Gateway.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Import-Module $PSScriptRoot\SdnDiag.Common.psm1 5 | Import-Module $PSScriptRoot\SdnDiag.Utilities.psm1 6 | 7 | $configurationData = Import-PowerShellDataFile -Path "$PSScriptRoot\SdnDiag.Gateway.Config.psd1" 8 | New-Variable -Name 'SdnDiagnostics_Gateway' -Scope 'Script' -Force -Value @{ 9 | Config = $configurationData 10 | } 11 | 12 | ########################## 13 | #### CLASSES & ENUMS ##### 14 | ########################## 15 | 16 | ########################## 17 | #### ARG COMPLETERS ###### 18 | ########################## 19 | 20 | ########################## 21 | ####### FUNCTIONS ######## 22 | ########################## 23 | 24 | function Get-GatewayConfigState { 25 | <# 26 | .SYNOPSIS 27 | Outputs a set of configuration state files for the gateway role. 28 | .PARAMETER OutputDirectory 29 | Specifies a specific path and folder in which to save the files. 30 | .EXAMPLE 31 | PS> Get-GatewayConfigState -OutputDirectory "C:\Temp\CSS_SDN" 32 | #> 33 | 34 | [CmdletBinding()] 35 | param ( 36 | [Parameter(Mandatory = $true)] 37 | [System.IO.FileInfo]$OutputDirectory 38 | ) 39 | 40 | $currentErrorActionPreference = $ErrorActionPreference 41 | $ProgressPreference = 'SilentlyContinue' 42 | $ErrorActionPreference = 'Ignore' 43 | [string]$outDir = Join-Path -Path $OutputDirectory.FullName -ChildPath "ConfigState\Gateway" 44 | 45 | try { 46 | $config = Get-SdnModuleConfiguration -Role 'Gateway' 47 | "Collect configuration state details for role {0}" -f $config.Name | Trace-Output 48 | if (-NOT (Initialize-DataCollection -Role 'Gateway' -FilePath $outDir -MinimumMB 100)) { 49 | "Unable to initialize environment for data collection" | Trace-Output -Level:Error 50 | return 51 | } 52 | 53 | [string]$regDir = Join-Path -Path $outDir -ChildPath "Registry" 54 | Export-RegistryKeyConfigDetails -Path $config.properties.regKeyPaths -OutputDirectory $regDir 55 | 56 | # dump out the role configuration state properties 57 | "Getting RRAS VPN configuration details" | Trace-Output -Level:Verbose 58 | Get-VpnServerConfiguration | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 59 | Get-VpnS2SInterface | Export-ObjectToFile -FilePath $outDir FileType txt -Format List 60 | Get-RemoteaccessRoutingDomain | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 61 | 62 | foreach ($routingDomain in Get-RemoteAccessRoutingDomain) { 63 | "Getting properties for routing domain {0}" -f $routingDomain.RoutingDomain | Trace-Output -Level:Verbose 64 | $routingDomainPath = New-Item -Path (Join-Path -Path $outDir -ChildPath $routingDomain.RoutingDomainID) -ItemType Directory -Force 65 | Get-BgpRouter -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -Name 'Get-BgpRouter' -FileType txt -Format List 66 | Get-BgpPeer -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -Name 'Get-BgpPeer' -FileType txt -Format List 67 | Get-BgprouteInformation -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -Name 'Get-BgprouteInformation' -FileType txt -Format List 68 | Get-BgpCustomRoute -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -Name 'Get-BgpCustomRoute' -FileType txt -Format List 69 | Get-BgpStatistics -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -Name 'Get-BgpStatistics' -FileType txt -Format List 70 | Get-BgpRoutingPolicy -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -Name 'Get-BgpRoutingPolicy' -FileType txt -Format List 71 | Get-BgpRouteFlapDampening -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -Name 'Get-BgpRouteFlapDampening' -FileType txt -Format List 72 | Get-BgpRouteAggregate -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -Name 'Get-BgpRouteAggregate' -FileType txt -Format List 73 | } 74 | 75 | # for ipsec fast path, there is a new service w/ new cmdlets to get the tunnels and routing domains 76 | if ((Get-Service -Name 'GatewayService').Status -ieq 'Running') { 77 | "GatewayService is enabled. Getting GatewayService configuration details" | Trace-Output -Level:Verbose 78 | $gatewayServicePath = New-Item -Path (Join-Path -Path $outDir -ChildPath 'GatewayService') -ItemType Directory -Force 79 | Get-Service -Name 'GatewayService' | Export-ObjectToFile -FilePath $gatewayServicePath.FullName -Prefix 'GatewayService' -Name 'Get-Service' -FileType txt -Format List 80 | Get-GatewayConfiguration | Export-ObjectToFile -FilePath $gatewayServicePath.FullName -Name 'Get-GatewayConfiguration' -FileType txt -Format List 81 | Get-GatewayRoutingDomain | Export-ObjectToFile -FilePath $gatewayServicePath.FullName -Name 'Get-GatewayRoutingDomain' -FileType txt -Format List 82 | Get-GatewayTunnel | Export-ObjectToFile -FilePath $gatewayServicePath.FullName -Name 'Get-GatewayTunnel' -FileType txt -Format List 83 | Get-GatewayTunnelStatistics | Export-ObjectToFile -FilePath $gatewayServicePath.FullName -Name 'Get-GatewayTunnelStatistics' -FileType txt -Format List 84 | 85 | foreach ($routingDomain in Get-GatewayRoutingDomain) { 86 | "Getting properties for routing domain {0}" -f $routingDomain.RoutingDomain | Trace-Output -Level:Verbose 87 | $routingDomainPath = New-Item -Path (Join-Path -Path $gatewayServicePath.FullName -ChildPath $routingDomain.RoutingDomain) -ItemType Directory -Force 88 | Get-BgpRouter -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -FileType txt -Format List 89 | Get-BgpPeer -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -FileType txt -Format List 90 | Get-BgpRouteInformation -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -FileType txt -Format List 91 | Get-BgpCustomRoute -RoutingDomain $routingDomain.RoutingDomain | Export-ObjectToFile -FilePath $routingDomainPath.FullName -FileType txt -Format List 92 | } 93 | } 94 | } 95 | catch { 96 | $_ | Trace-Exception 97 | $_ | Write-Error 98 | } 99 | 100 | $ProgressPreference = 'Continue' 101 | $ErrorActionPreference = $currentErrorActionPreference 102 | } 103 | 104 | function Get-GatewayModuleConfig { 105 | return $Script:SdnDiagnostics_Gateway.Config 106 | } 107 | 108 | function Disable-SdnRasGatewayTracing { 109 | <# 110 | .SYNOPSIS 111 | Disable netsh tracing for the RAS components 112 | #> 113 | 114 | try { 115 | # since there has not been a time when this as returned an error, just invoking the expression and not doing any error handling 116 | Invoke-Expression -Command "netsh ras set tracing * disabled" 117 | 118 | Start-Sleep -Seconds 5 119 | $files = Get-Item -Path "$($config.properties.commonPaths.rasGatewayTraces)\*" -Include '*.log','*.etl' 120 | 121 | $object = New-Object -TypeName PSCustomObject -Property ( 122 | [Ordered]@{ 123 | Status = 'Stopped' 124 | Files = $files.FullName 125 | } 126 | ) 127 | 128 | return $object 129 | } 130 | catch { 131 | $_ | Trace-Exception 132 | $_ | Write-Error 133 | } 134 | } 135 | 136 | function Enable-SdnRasGatewayTracing { 137 | <# 138 | .SYNOPSIS 139 | Enables netsh tracing for the RAS components. Files will be saved to C:\Windows\Tracing by default 140 | #> 141 | 142 | try { 143 | # ensure that the appropriate windows feature is installed and ensure module is imported 144 | $config = Get-SdnModuleConfiguration -Role 'Gateway' 145 | if (-NOT (Initialize-DataCollection -Role 'Gateway' -FilePath $config.properties.commonPaths.rasGatewayTraces -MinimumMB 250)) { 146 | "Unable to initialize environment for data collection" | Trace-Output -Level:Error 147 | return 148 | } 149 | 150 | # remove any previous or stale logs 151 | $files = Get-Item -Path "$($config.properties.commonPaths.rasGatewayTraces)\*" -Include '*.log','*.etl' | Where-Object {$_.LastWriteTime -le (Get-Date).AddHours(-1)} 152 | if($files){ 153 | "Cleaning up files from previous collections" | Trace-Output -Level:Verbose 154 | $files | Remove-Item -Force 155 | } 156 | 157 | # enable ras tracing 158 | $expression = Invoke-Expression -Command "netsh ras set tracing * enabled" 159 | if($expression -ilike "*Unable to start ETW*"){ 160 | $msg = $expression[1] 161 | throw New-Object -TypeName System.Exception($msg) 162 | } 163 | else { 164 | $object = New-Object -TypeName PSCustomObject -Property ( 165 | [Ordered]@{ 166 | Status = 'Running' 167 | } 168 | ) 169 | } 170 | 171 | return $object 172 | } 173 | catch { 174 | $_ | Trace-Exception 175 | $_ | Write-Error 176 | } 177 | } 178 | 179 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.Health.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright='© Microsoft Corporation. All rights reserved.' 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | HealthValidations = @{ 6 | 7 | # COMMON TESTS 8 | 9 | 'Test-SdnDiagnosticsCleanupTaskEnabled' = @{ 10 | Description = "Scheduled task is not enabled on the SDN infrastructure node(s)." 11 | Impact = "Unconstrained log files may grow and consume disk space." 12 | PublicDocUrl = "" 13 | } 14 | 'Test-SdnNetworkControllerApiNameResolution' = @{ 15 | Description = "Network Controller URL is not resolvable." 16 | Impact = "Calls to Network Controller NB API will fail resulting in policy configuration failures and unable to manage SDN resources." 17 | PublicDocUrl = "" 18 | } 19 | 'Test-SdnNonSelfSignedCertificateInTrustedRootStore' = @{ 20 | Description = "Non Root Cert exist in Host Trusted Root CA Store" 21 | Impact = "Network Controller will have issues communicating Host's TCP 6640 and 443 port with certificate error." 22 | PublicDocUrl = "" 23 | } 24 | 'Test-SdnServiceState' = @{ 25 | Description = "Identified service(s) are not running on the SDN infrastructure node(s)." 26 | Impact = "SDN services and functionality will be impacted without the service running." 27 | PublicDocUrl = "" 28 | } 29 | 30 | # GATEWAY TESTS 31 | 32 | 33 | # LOAD BALANCER MUX TESTS 34 | 35 | 'Test-SdnMuxConnectionStateToRouter' = @{ 36 | Description = "One or more Load Balancer Muxes do not have an active BGP connection via TCP port 179 to the switch." 37 | Impact = "Public IP addresses may not be routable as Load Balancer Muxes are not advertising the public IP addresses to the switch." 38 | PublicDocUrl = "https://learn.microsoft.com/en-us/azure-stack/hci/manage/troubleshoot-software-load-balancer" 39 | } 40 | 'Test-SdnMuxConnectionStateToSlbManager' = @{ 41 | Description = "SLB Manager does not have connectivity established to Mux(es) via TCP 8560." 42 | Impact = "SLB Manager will not be able to program VIP:DIP mappings to the Load Balancer Mux(es) which will impact routing of Virtual IPs." 43 | PublicDocUrl = "https://learn.microsoft.com/en-us/azure-stack/hci/manage/troubleshoot-software-load-balancer" 44 | } 45 | 46 | # NETWORK CONTROLLER TESTS 47 | 48 | 'Test-SdnNetworkControllerNodeRestInterface' = @{ 49 | Description = "Network Controller node(s) are missing the Network Adapter that is required for the REST interface." 50 | Impact = "Failover of the NB API will not occur if the Network Controller node(s) are missing the Network Adapter that is required for the REST interface." 51 | PublicDocUrl = "https://learn.microsoft.com/en-us/powershell/module/networkcontroller/set-networkcontrollernode" 52 | } 53 | 'Test-SdnServiceFabricApplicationHealth' = @{ 54 | Description = "Network Controller application with Service Fabric is not healthy." 55 | Impact = "Network Controller services and functionality may be impacted." 56 | PublicDocUrl = "" 57 | } 58 | 'Test-SdnServiceFabricClusterHealth' = @{ 59 | Description = "Service Fabric cluster for Network Controller is not healthy." 60 | Impact = "Network Controller services and functionality may be impacted." 61 | PublicDocUrl = "" 62 | } 63 | 'Test-SdnServiceFabricNodeStatus' = @{ 64 | Description = "Service Fabric node(s) are offline and not participating in the cluster." 65 | Impact = "Minimum amount of nodes are required to maintain quorum and cluster availability. Services will be in read-only state if quorum is lost and may result in data loss." 66 | PublicDocUrl = "https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-disaster-recovery" 67 | } 68 | 'Test-ResourceConfigurationState' = @{ 69 | Description = "Infrastructure resource configuration is not Succeeded." 70 | Impact = "SDN services and functionality may be impacted." 71 | PublicDocUrl = "https://learn.microsoft.com/en-us/windows-server/networking/sdn/troubleshoot/troubleshoot-windows-server-software-defined-networking-stack#hoster-validate-system-health" 72 | } 73 | 'Test-ResourceProvisioningState' = @{ 74 | Description = "Infrastructure resource provisioning is not Succeeded." 75 | Impact = "SDN services and functionality may be impacted." 76 | PublicDocUrl = "https://learn.microsoft.com/en-us/windows-server/networking/sdn/troubleshoot/troubleshoot-windows-server-software-defined-networking-stack#hoster-validate-system-health" 77 | } 78 | 'Test-NetworkInterfaceAPIDuplicateMacAddress' = @{ 79 | Description = "Duplicate MAC address detected within the API." 80 | Impact = "Policy configuration failures may be reported by Network Controller when applying policies to the Hyper-v host. Network Interfaces reporting configurationState failure will not be routable." 81 | PublicDocUrl = "" 82 | } 83 | 84 | # SERVER TESTS 85 | 86 | 'Test-SdnEncapOverhead' = @{ 87 | Description = "EncapOverhead/JumboPacket is not configured properly on the Hyper-V Hosts" 88 | Impact = "Intermittent packet loss may occur under certain conditions when routing traffic within the logical network." 89 | PublicDocUrl = "https://learn.microsoft.com/en-us/windows-server/networking/sdn/troubleshoot/troubleshoot-windows-server-software-defined-networking-stack#check-mtu-and-jumbo-frame-support-on-hnv-provider-logical-network" 90 | } 91 | 'Test-SdnHostAgentConnectionStateToApiService' = @{ 92 | Description = "Network Controller Host Agent is not connected to the Network Controller API Service." 93 | Impact = "Policy configuration may not be pushed to the Hyper-V host(s) if no southbound connectivity is available." 94 | PublicDocUrl = "" 95 | } 96 | 'Test-SdnProviderNetwork' = @{ 97 | Description = "Logical network does not support VXLAN or NVGRE encapsulated traffic" 98 | Impact = "Intermittent packet loss may occur under certain conditions when routing traffic within the logical network." 99 | PublicDocUrl = "https://learn.microsoft.com/en-us/windows-server/networking/sdn/troubleshoot/troubleshoot-windows-server-software-defined-networking-stack#check-mtu-and-jumbo-frame-support-on-hnv-provider-logical-network" 100 | } 101 | 'Test-VfpDuplicateMacAddress' = @{ 102 | Description = "Duplicate MAC address detected within Virtual Filtering Platform (VFP)." 103 | Impact = "Policy configuration failures may be reported by Network Controller when applying policies to the Hyper-v host. In addition, network traffic may be impacted." 104 | PublicDocUrl = "" 105 | } 106 | 'Test-SdnVfpEnabledVMSwitch' = @{ 107 | Description = "No VMSwitches detected with VFP enabled on the Hyper-V host(s)." 108 | Impact = "Policy configuration failures may be reported by Network Controller when applying policies to the Hyper-v host." 109 | PublicDocUrl = "" 110 | } 111 | 'Test-SdnVfpEnabledVMSwitchMultiple' = @{ 112 | Description = "Multiple VFP enabled virtual switches detected on the Hyper-V host(s)." 113 | Impact = "Policy configuration failures may be reported by Network Controller when applying policies to the Hyper-v host." 114 | PublicDocUrl = "" 115 | } 116 | 'Test-VMNetAdapterDuplicateMacAddress' = @{ 117 | Description = "Duplicate MAC address detected with the data plane on the Hyper-V host(s)." 118 | Impact = "Policy configuration failures may be reported by Network Controller when applying policies to the Hyper-v host. In addition, network traffic may be impacted for the interfaces that are duplicated." 119 | PublicDocUrl = "" 120 | } 121 | 'Test-ServerHostId' = @{ 122 | Description = "HostID is not configured properly on the Hyper-V Hosts" 123 | Impact = "Mismatch of HostId between Hyper-V host(s) and Network Controller will result in policy configuration failures." 124 | PublicDocUrl = "https://learn.microsoft.com/en-us/windows-server/networking/sdn/troubleshoot/troubleshoot-windows-server-software-defined-networking-stack#check-for-corresponding-hostids-and-certificates-between-network-controller-and-each-hyper-v-host" 125 | } 126 | } 127 | ConfigurationStateErrorCodes = @{ 128 | 'Unknown' = @{ 129 | Message = 'Unknown error' 130 | Action = 'Collect the logs and contact Microsoft Support' 131 | } 132 | 'HostUnreachable' = @{ 133 | Message = 'The host machine is not reachable' 134 | Action = 'Check the Management network connectivity between Network Controller and Host' 135 | } 136 | 'PAIpAddressExhausted' = @{ 137 | Message = 'The PA Ip addresses exhausted' 138 | Action = 'Increase the HNV Provider logical subnet''s IP Pool Size' 139 | } 140 | 'PAMacAddressExhausted' = @{ 141 | Message = 'The PA Mac addresses exhausted' 142 | Action = 'Increase the Mac Pool Range' 143 | } 144 | 'PAAddressConfigurationFailure' = @{ 145 | Message = 'Failed to plumb PA addresses to the host' 146 | Action = 'Check the management network connectivity between Network Controller and Host.' 147 | } 148 | 'CertificateNotTrusted' = @{ 149 | Message = 'Certificate is not trusted' 150 | Action = 'Fix the certificates used for communication with the host.' 151 | } 152 | 'CertificateNotAuthorized' = @{ 153 | Message = 'Certificate not authorized' 154 | Action = 'Fix the certificates used for communication with the host.' 155 | } 156 | 'PolicyConfigurationFailureOnVfp' = @{ 157 | Message = 'Failure in configuring VFP policies' 158 | Action = 'This is a runtime failure. No definite workarounds. Collect logs.' 159 | } 160 | 'HostNotConnectedToController' = @{ 161 | Message = 'The Host is not yet connected to the Network Controller' 162 | Action = 'Validate that Host is online and operational, NCHostAgent service is started and HostID registry key matches the Instance ID of the server resource' 163 | } 164 | 'MultipleVfpEnabledSwitches' = @{ 165 | Message = 'There are multiple VFp enabled Switches on the host' 166 | Action = 'Delete one of the switches, since Network Controller Host Agent only supports one vSwitch with the VFP extension enabled' 167 | } 168 | 'PolicyConfigurationFailure' = @{ 169 | Message = 'Failed to push policies (vSwitch, vNet, ACL) for a VmNic due to certificate errors or connectivity errors' 170 | Action = 'Check if proper certificates have been deployed (Certificate subject name must match FQDN of host). Also verify the host connectivity with the Network Controller' 171 | } 172 | 'DistributedRouterConfigurationFailure' = @{ 173 | Message = 'Failed to configure the Distributed router settings on the host vNic' 174 | Action = 'TCPIP stack error. May require cleaning up the PA and DR Host vNICs on the server on which this error was reported' 175 | } 176 | 'DhcpAddressAllocationFailure' = @{ 177 | Message = 'DHCP address allocation failed for a VMNic' 178 | Action = 'Check if the static IP address attribute is configured on the NIC resource' 179 | } 180 | 'CertificateNotTrusted CertificateNotAuthorized' = @{ 181 | Message = 'Failed to connect to Mux due to network or cert errors' 182 | Action = 'Check the numeric code provided in the error message code: this corresponds to the winsock error code. Certificate errors are granular (for example, cert cannot be verified, cert not authorized, etc.)' 183 | } 184 | 'PortBlocked' = @{ 185 | Message = 'The VFP port is blocked, due to lack of VNET / ACL policies' 186 | Action = 'Check if there are any other errors, which might cause the policies to be not configured.' 187 | } 188 | 'Overloaded' = @{ 189 | Message = 'Loadbalancer MUX is overloaded' 190 | Action = 'Performance issue with MUX' 191 | } 192 | 'RoutePublicationFailure' = @{ 193 | Message = 'Loadbalancer MUX is not connected to a BGP router' 194 | Action = 'Check if the MUX has connectivity with the BGP routers and that BGP peering is setup correctly' 195 | } 196 | 'VirtualServerUnreachable' = @{ 197 | Message = 'Loadbalancer MUX is not connected to SLB manager' 198 | Action = 'Check connectivity between SLBM and MUX' 199 | } 200 | 'QosConfigurationFailure' = @{ 201 | Message = 'Failed to configure QOS policies' 202 | Action = 'See if sufficient bandwidth is available for all VM''s if QOS reservation is used' 203 | } 204 | } 205 | 206 | HealthFaultEnabled = $false 207 | HealthFaultSupportedBuilds = @( 208 | '23H2' # Build Number 25398 209 | '24H2' # Build Number 26100 210 | ) 211 | HealthFaultSupportedProducts = @( 212 | 'Azure Stack HCI' 213 | 'Windows Server 2025 Datacenter' 214 | ) 215 | } 216 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.LoadBalancerMux.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Name = "LoadBalancerMux" 6 | WindowsFeature = @( 7 | "SoftwareLoadBalancer" 8 | ) 9 | RequiredModules = @() 10 | Properties = @{ 11 | CommonPaths = @{} 12 | EventLogProviders = @( 13 | "Microsoft-Windows-SlbMux*" 14 | ) 15 | RegKeyPaths = @( 16 | "HKLM:\Software\Microsoft\NetworkController" 17 | "HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent" 18 | "HKLM:\SYSTEM\CurrentControlSet\Services\SlbMux" 19 | "HKLM:\SYSTEM\CurrentControlSet\Services\SlbMuxDriver" 20 | ) 21 | Services = @{ 22 | SlbMux = @{ 23 | DisplayName = "Software Load Balancer Host Agent" 24 | } 25 | } 26 | EtwTraceProviders = @{ 27 | SlbMux = @{ 28 | isOptional = $false 29 | Providers = @( 30 | "{645b8679-5451-4c36-9857-a90e7dbf97bc}" 31 | "{6C2350F8-F827-4B74-AD0C-714A92E22576}" 32 | "{2E67FCF3-C48E-4B2D-A689-A91D07EDB910}" 33 | "{9FD2B528-8D3D-42D0-8FDF-5B1998004278}" 34 | ) 35 | Level = $null 36 | Keywords = $null 37 | } 38 | } 39 | } 40 | ResourceName = "LoadBalancerMuxes" 41 | } 42 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.LoadBalancerMux.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Import-Module $PSScriptRoot\SdnDiag.Common.psm1 5 | Import-Module $PSScriptRoot\SdnDiag.Utilities.psm1 6 | 7 | $configurationData = Import-PowerShellDataFile -Path "$PSScriptRoot\SdnDiag.LoadBalancerMux.Config.psd1" 8 | New-Variable -Name 'SdnDiagnostics_SLB' -Scope 'Script' -Force -Value @{ 9 | Config = $configurationData 10 | } 11 | 12 | ########################## 13 | #### CLASSES & ENUMS ##### 14 | ########################## 15 | 16 | class MuxConfig { 17 | [ipaddress]$SourceIP4Address 18 | [ipaddress]$SourceIP6Address 19 | [int]$MaxFlowEntries 20 | [int]$FlowIdleTimeout 21 | [int]$HalfFlowIdleTimeout 22 | [int]$FlowEntriesWatermark 23 | [int]$CoeffecientForMovingAverage 24 | [int]$BandwidthCalculationTimeInterval 25 | [int]$AggregateBandwidthWatermark 26 | [int]$ElephantBandwidthThreshold 27 | [int]$AggregateBandwidthLimitForElephant 28 | [int]$MaxDropProbability 29 | [int]$MaxBandwidthUtilizationForDrop 30 | [int]$InitialHashTableSize 31 | [int]$MaxHashTableUsagePct 32 | [int]$MinHashTableUsagePct 33 | [int]$HashTableResizeFactor 34 | [int]$HashPrimeNumber 35 | [int]$FlowSamplingIntervalInSec 36 | [string]$MUXFlags 37 | } 38 | 39 | class MuxStatistics { 40 | [string]$Type 41 | [int]$TotalPackets 42 | [int]$SynPackets 43 | [int]$PacketsPerSecond 44 | [int]$DroppedPackets 45 | [int]$TotalBytes 46 | [int]$FlowEntries 47 | [int]$DroppedFlowEntries 48 | [int]$TotalNumberOfHashTableBuckets 49 | $FlowEntriesLimitUtilization 50 | $FlowEntriesWatermarkUtilization 51 | $AverageBandwidth 52 | $BandwidthLimitUtilization 53 | $BandwidthWatermarkUtilization 54 | [int]$ElephantCount 55 | [int]$FlowCacheMisses 56 | } 57 | 58 | ########################## 59 | #### ARG COMPLETERS ###### 60 | ########################## 61 | 62 | ########################## 63 | ####### FUNCTIONS ######## 64 | ########################## 65 | 66 | function Get-MuxDriverControl { 67 | if (-NOT (Get-Module -Name 'Microsoft.Cloudnet.Slb.Mux.MuxDriverControl')) { 68 | Import-Module "$env:SystemRoot\System32\Microsoft.Cloudnet.Slb.Mux.MuxDriverControl.dll" -Force 69 | } 70 | 71 | return ([Microsoft.Cloudnet.Slb.Mux.Driver.SlbDriverControl]::new()) 72 | } 73 | 74 | function Get-SlbMuxConfigState { 75 | <# 76 | .SYNOPSIS 77 | Outputs a set of configuration state files for the load balancer role. 78 | .PARAMETER OutputDirectory 79 | Specifies a specific path and folder in which to save the files. 80 | .EXAMPLE 81 | PS> Get-SlbMuxConfigState -OutputDirectory "C:\Temp\CSS_SDN" 82 | #> 83 | 84 | [CmdletBinding()] 85 | param ( 86 | [Parameter(Mandatory = $true)] 87 | [System.IO.FileInfo]$OutputDirectory 88 | ) 89 | 90 | $currentErrorActionPreference = $ErrorActionPreference 91 | $ProgressPreference = 'SilentlyContinue' 92 | $ErrorActionPreference = 'Ignore' 93 | [string]$outDir = Join-Path -Path $OutputDirectory.FullName -ChildPath "ConfigState/LoadBalancerMux" 94 | 95 | try { 96 | $config = Get-SdnModuleConfiguration -Role 'LoadBalancerMux' 97 | "Collect configuration state details for role {0}" -f $config.Name | Trace-Output 98 | if (-NOT (Initialize-DataCollection -Role $config.Name -FilePath $outDir -MinimumMB 20)) { 99 | "Unable to initialize environment for data collection" | Trace-Output -Level:Error 100 | return 101 | } 102 | 103 | [string]$regDir = Join-Path -Path $outDir -ChildPath "Registry" 104 | Export-RegistryKeyConfigDetails -Path $config.properties.regKeyPaths -OutputDirectory $regDir 105 | 106 | # output slb configuration and states 107 | "Getting MUX Driver Control configuration settings" | Trace-Output -Level:Verbose 108 | Get-SdnMuxState | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 109 | Get-SdnMuxDistributedRouterIP | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 110 | Get-SdnMuxStatefulVip | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 111 | Get-SdnMuxStatelessVip | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 112 | Get-SdnMuxStats | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 113 | Get-SdnMuxVip | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 114 | Get-SdnMuxVipConfig | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 115 | } 116 | catch { 117 | $_ | Trace-Exception 118 | $_ | Write-Error 119 | } 120 | 121 | $ProgressPreference = 'Continue' 122 | $ErrorActionPreference = $currentErrorActionPreference 123 | } 124 | 125 | function Get-SdnMuxCertificate { 126 | <# 127 | .SYNOPSIS 128 | Returns the certificate used by the SDN Load Balancer Mux. 129 | #> 130 | 131 | [CmdletBinding()] 132 | param () 133 | 134 | try { 135 | $muxCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\SlbMux' -Name 'MuxCert' 136 | $subjectName = "CN={0}" -f $muxCert 137 | $certificate = Get-SdnCertificate -Subject $subjectName -Path 'Cert:\LocalMachine\My' -NetworkControllerOid 138 | return $certificate 139 | } 140 | catch { 141 | $_ | Trace-Exception 142 | $_ | Write-Error 143 | } 144 | } 145 | 146 | function Get-SdnMuxDistributedRouterIP { 147 | <# 148 | .SYNOPSIS 149 | This cmdlet returns the Distributed Router IPs that are advertised on the MUX. 150 | .DESCRIPTION 151 | .PARAMETER VirtualIP 152 | The Virtual IP address (VIP) of the resource. If omitted, will return all VIPs programmed within the MUX driver. 153 | .EXAMPLE 154 | PS> Get-SdnMuxDistributedRouterIP 155 | .EXAMPLE 156 | PS> Get-SdnMuxDistributedRouterIP -VirtualIP 100.90.95.42 157 | #> 158 | 159 | [CmdletBinding()] 160 | param ( 161 | [Parameter(Mandatory = $false)] 162 | [System.String]$VirtualIP 163 | ) 164 | 165 | try { 166 | $control = Get-MuxDriverControl 167 | 168 | $vipConfig = [System.Collections.Generic.List[Microsoft.Cloudnet.Slb.Mux.Driver.VipConfig]]::new() 169 | $control.GetDrips($null , [ref]$vipConfig) 170 | 171 | if ($VirtualIP) { 172 | return ($vipConfig | Where-Object {$_.AddressStr -ieq $VirtualIP}) 173 | } 174 | else { 175 | return $vipConfig 176 | } 177 | } 178 | catch { 179 | $_ | Trace-Exception 180 | $_ | Write-Error 181 | } 182 | } 183 | 184 | function Get-SdnMuxState { 185 | <# 186 | .SYNOPSIS 187 | This cmdlet retrieves the current state of the load balancer MUX. 188 | .DESCRIPTION 189 | #> 190 | 191 | try { 192 | return (Get-MuxDriverControl) 193 | } 194 | catch { 195 | $_ | Trace-Exception 196 | $_ | Write-Error 197 | } 198 | } 199 | 200 | function Get-SdnMuxConfig { 201 | $muxConfig = [MuxConfig]::new() 202 | $results = muxdrivercontrolconsole /GetMuxConfig 203 | foreach ($i in $results) { 204 | if ([string]::IsNullOrEmpty($i)) { continue } 205 | if ($i.contains(":")){ 206 | $property = $i.Split(":")[0].Trim().Replace(" ", "") 207 | $value = $i.Split(":")[1].Trim() 208 | 209 | if ($property -iin $muxConfig.PSObject.Properties.Name) { 210 | $muxConfig.$property = $value 211 | } 212 | } 213 | } 214 | 215 | return $muxConfig 216 | } 217 | 218 | function Get-SdnMuxStats { 219 | $array = @() 220 | $results = muxdrivercontrolconsole /GetMuxStats 221 | 222 | foreach ($i in $results) { 223 | if ([string]::IsNullOrEmpty($i)) { continue } 224 | 225 | if ($i.contains(":")){ 226 | $property = $i.Split(":")[0].Trim().Replace(" ", "") 227 | $value = $i.Split(":")[1].Trim() 228 | 229 | if ($property -ilike "MuxStatisticsfor*") { 230 | if ($muxStatistics) { 231 | $array += $muxStatistics 232 | } 233 | 234 | switch ($property) { 235 | "MuxStatisticsforIPv4Traffic" { 236 | $muxStatistics = [MuxStatistics]@{ 237 | Type = "IPv4" 238 | } 239 | } 240 | "MuxStatisticsforIPv6Traffic" { 241 | $muxStatistics = [MuxStatistics]@{ 242 | Type = "IPv6" 243 | } 244 | } 245 | } 246 | } 247 | 248 | if ($property -iin $muxStatistics.PSObject.Properties.Name) { 249 | $muxStatistics.$property = $value 250 | } 251 | } 252 | } 253 | 254 | if ($muxStatistics) { 255 | $array += $muxStatistics 256 | } 257 | 258 | return $array 259 | } 260 | 261 | function Get-SdnMuxStatefulVip { 262 | <# 263 | .SYNOPSIS 264 | Gets details related to the stateful VIPs. 265 | .DESCRIPTION 266 | .PARAMETER VirtualIP 267 | The Virtual IP address (VIP) of the resource. If omitted, will return all VIPs programmed within the MUX driver. 268 | .EXAMPLE 269 | PS> Get-SdnMuxStatefulVip 270 | .EXAMPLE 271 | PS> Get-SdnMuxStatefulVip -VirtualIP 100.90.95.42 272 | #> 273 | 274 | [CmdletBinding()] 275 | param ( 276 | [Parameter(Mandatory = $false)] 277 | [System.String]$VirtualIP 278 | ) 279 | 280 | try { 281 | $control = Get-MuxDriverControl 282 | $statefulVips = [System.Collections.Generic.List[Microsoft.Cloudnet.Slb.Mux.Driver.VipEndpointKey]]::new() 283 | 284 | $control.GetStatefulVips($null, [ref]$statefulVips) 285 | 286 | if ($VirtualIP) { 287 | return ($statefulVips | Where-Object {$_.AddressStr -ieq $VirtualIP}) 288 | } 289 | else { 290 | return $statefulVips 291 | } 292 | } 293 | catch { 294 | $_ | Trace-Exception 295 | $_ | Write-Error 296 | } 297 | } 298 | 299 | function Get-SdnMuxStatelessVip { 300 | <# 301 | .SYNOPSIS 302 | Gets details related to the stateless VIPs. 303 | .DESCRIPTION 304 | .PARAMETER VirtualIP 305 | The Virtual IP address (VIP) of the resource. If omitted, will return all VIPs programmed within the MUX driver. 306 | .EXAMPLE 307 | PS> Get-SdnMuxStatelessVip 308 | .EXAMPLE 309 | PS> Get-SdnMuxStatelessVip -VirtualIP 100.90.95.42 310 | #> 311 | 312 | [CmdletBinding()] 313 | param ( 314 | [Parameter(Mandatory = $false)] 315 | [System.String]$VirtualIP 316 | ) 317 | 318 | try { 319 | $control = Get-MuxDriverControl 320 | $statelessVips = [System.Collections.Generic.List[Microsoft.Cloudnet.Slb.Mux.Driver.VipEndpointKey]]::new() 321 | 322 | $control.GetStatelessVips($null, [ref]$statelessVips) 323 | 324 | if ($VirtualIP) { 325 | return ($statelessVips | Where-Object {$_.AddressStr -ieq $VirtualIP}) 326 | } 327 | else { 328 | return $statelessVips 329 | } 330 | } 331 | catch { 332 | $_ | Trace-Exception 333 | $_ | Write-Error 334 | } 335 | } 336 | 337 | function Get-SdnMuxStats { 338 | <# 339 | .SYNOPSIS 340 | Get the statistics related to the Virtual IPs. 341 | .DESCRIPTION 342 | .PARAMETER VirtualIP 343 | The Virtual IP address (VIP) of the resource. If omitted, will return all VIPs programmed within the MUX driver. 344 | .PARAMETER SkipReset 345 | .EXAMPLE 346 | PS> Get-SdnMuxStats 347 | .EXAMPLE 348 | PS> Get-SdnMuxStats -VirtualIP 100.90.95.42 349 | #> 350 | 351 | [CmdletBinding()] 352 | param ( 353 | [Parameter(Mandatory = $false)] 354 | [System.String]$VirtualIP, 355 | 356 | [Parameter(Mandatory = $false)] 357 | [System.Boolean]$SkipReset = $true 358 | ) 359 | 360 | try { 361 | $control = Get-MuxDriverControl 362 | return ($control.GetGlobalStats($SkipReset)) 363 | } 364 | catch { 365 | $_ | Trace-Exception 366 | $_ | Write-Error 367 | } 368 | } 369 | 370 | function Get-SdnMuxVip { 371 | <# 372 | .SYNOPSIS 373 | This cmdlet returns the VIP endpoint(s). 374 | .DESCRIPTION 375 | .PARAMETER VirtualIP 376 | The Virtual IP address (VIP) of the resource. If omitted, will return all VIPs programmed within the MUX driver. 377 | .EXAMPLE 378 | PS> Get-SdnMuxVip 379 | .EXAMPLE 380 | PS> Get-SdnMuxVip -VirtualIP 100.90.95.42 381 | #> 382 | 383 | [CmdletBinding()] 384 | param ( 385 | [Parameter(Mandatory = $false)] 386 | [System.String]$VirtualIP 387 | ) 388 | 389 | try { 390 | $control = Get-MuxDriverControl 391 | $vipConfig = [System.Collections.Generic.List[Microsoft.Cloudnet.Slb.Mux.Driver.VipConfig]]::new() 392 | 393 | $control.GetVips($null, [ref]$vipConfig) 394 | 395 | if ($VirtualIP) { 396 | return ($vipConfig | Where-Object {$_.AddressStr -ieq $VirtualIP}) 397 | } 398 | else { 399 | return $vipConfig 400 | } 401 | } 402 | catch { 403 | $_ | Trace-Exception 404 | $_ | Write-Error 405 | } 406 | } 407 | 408 | function Get-SdnMuxVipConfig { 409 | <# 410 | .SYNOPSIS 411 | Get configuration details such as the DIPs of the backend resources related to Virtual IP 412 | .PARAMETER VirtualIP 413 | The Virtual IP address (VIP) of the resource. If omitted, will return all VIPs programmed within the MUX driver. 414 | .EXAMPLE 415 | PS> Get-SdnMuxVipConfig 416 | .EXAMPLE 417 | PS> Get-SdnMuxVipConfig -VirtualIP 100.90.95.42 418 | #> 419 | 420 | [CmdletBinding()] 421 | param ( 422 | [Parameter(Mandatory = $false)] 423 | [System.String]$VirtualIP 424 | ) 425 | 426 | try { 427 | $control = Get-MuxDriverControl 428 | $list = [System.Collections.Generic.List[Microsoft.Cloudnet.Slb.Mux.Driver.VipEndpointConfig]]::new() 429 | 430 | if ($VirtualIP) { 431 | $statefulVips = Get-SdnMuxStatefulVip -VirtualIp $VirtualIP 432 | } 433 | else { 434 | $statefulVips = Get-SdnMuxStatefulVip 435 | } 436 | 437 | foreach ($vip in $statefulVips) { 438 | $vipConfig = New-Object -Type Microsoft.Cloudnet.Slb.Mux.Driver.VipEndpointConfig 439 | $control.GetVipConfig($vip, [ref]$vipConfig) 440 | 441 | [void]$list.Add($vipConfig) 442 | } 443 | 444 | return $list 445 | } 446 | catch { 447 | $_ | Trace-Exception 448 | $_ | Write-Error 449 | } 450 | } 451 | 452 | function New-SdnMuxCertificate { 453 | <# 454 | .SYNOPSIS 455 | Generate new self-signed certificate to be used by Load Balancer Mux and distributes to the Network Controller(s) within the environment. 456 | .PARAMETER NotAfter 457 | Specifies the date and time, as a DateTime object, that the certificate expires. To obtain a DateTime object, use the Get-Date cmdlet. The default value for this parameter is one year after the certificate was created. 458 | .PARAMETER Path 459 | Specifies the file path location where a .cer file is exported automatically. 460 | .PARAMETER FabricDetails 461 | The SDN Fabric details derived from Get-SdnInfrastructureInfo. 462 | .PARAMETER Credential 463 | Specifies a user account that has permission to perform this action. The default is the current user 464 | .EXAMPLE 465 | New-SdnMuxCertificate -NotAfter (Get-Date).AddYears(1) -FabricDetails $Global:SdnDiagnostics.EnvironmentInfo 466 | #> 467 | 468 | [CmdletBinding()] 469 | param ( 470 | [Parameter(Mandatory = $false)] 471 | [datetime]$NotAfter = (Get-Date).AddYears(3), 472 | 473 | [Parameter(Mandatory = $false)] 474 | [System.String]$Path = "$(Get-WorkingDirectory)\MuxCert_{0}" -f (Get-FormattedDateTimeUTC), 475 | 476 | [Parameter(Mandatory = $false)] 477 | [System.Object]$FabricDetails, 478 | 479 | [System.Management.Automation.PSCredential] 480 | [System.Management.Automation.Credential()] 481 | $Credential = [System.Management.Automation.PSCredential]::Empty 482 | ) 483 | 484 | $config = Get-SdnModuleConfiguration -Role 'LoadBalancerMux' 485 | $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature 486 | if (-NOT ($confirmFeatures)) { 487 | throw New-Object System.NotSupportedException("The current machine is not a LoadBalancerMux, run this on LoadBalancerMux.") 488 | } 489 | 490 | # ensure that the module is running as local administrator 491 | Confirm-IsAdmin 492 | 493 | try { 494 | if (-NOT (Test-Path -Path $Path -PathType Container)) { 495 | "Creating directory {0}" -f $Path | Trace-Output 496 | $CertPath = New-Item -Path $Path -ItemType Directory -Force 497 | } 498 | else { 499 | $CertPath = Get-Item -Path $Path 500 | } 501 | 502 | $muxCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\SlbMux' -Name 'MuxCert' 503 | $subjectName = "CN={0}" -f $muxCert 504 | $certificate = New-SdnSelfSignedCertificate -Subject $subjectName -NotAfter $NotAfter 505 | 506 | # after the certificate has been generated, we want to export the certificate and save the file to directory 507 | # This allows the rest of the function to pick up these files and perform the steps as normal 508 | [System.String]$cerFilePath = "$(Join-Path -Path $CertPath.FullName -ChildPath $subjectName.ToString().ToLower().Replace('.','_').Replace("=",'_').Trim()).cer" 509 | "Exporting certificate to {0}" -f $cerFilePath | Trace-Output 510 | $exportedCertificate = Export-Certificate -Cert $certificate -FilePath $cerFilePath -Type CERT 511 | Copy-CertificateToFabric -CertFile $exportedCertificate.FullName -FabricDetails $FabricDetails -LoadBalancerMuxNodeCert -Credential $Credential 512 | 513 | $certObject = [PSCustomObject]@{ 514 | Certificate = $certificate 515 | FileInfo = $exportedCertificate 516 | } 517 | 518 | return $certObject 519 | } 520 | catch { 521 | $_ | Trace-Exception 522 | $_ | Write-Error 523 | } 524 | } 525 | 526 | function Start-SdnMuxCertificateRotation { 527 | <# 528 | .SYNOPSIS 529 | Performs a certificate rotation operation for the Load Balancer Muxes. 530 | .PARAMETER Credential 531 | Specifies a user account that has permission to perform this action on the Load Balancer Mux and Network Controller nodes. The default is the current user. 532 | .PARAMETER NcRestCertificate 533 | Specifies the client certificate that is used for a secure web request to Network Controller REST API. 534 | Enter a variable that contains a certificate or a command or expression that gets the certificate. 535 | .PARAMETER NcRestCredential 536 | Specifies a user account that has permission to perform this action against the Network Controller REST API. The default is the current user. 537 | .PARAMETER CertPath 538 | Path directory where certificate(s) .pfx files are located for use with certificate rotation. 539 | .PARAMETER GenerateCertificate 540 | Switch to determine if certificate rotate function should generate self-signed certificates. 541 | .PARAMETER CertPassword 542 | SecureString password for accessing the .pfx files, or if using -GenerateCertificate, what the .pfx files will be encrypted with. 543 | .PARAMETER NotAfter 544 | Expiration date when using -GenerateCertificate. If ommited, defaults to 3 years. 545 | .PARAMETER CertRotateConfig 546 | The Config generated by New-SdnCertificateRotationConfig to include appropriate certificate thumbprints for mux nodes. 547 | .PARAMETER Force 548 | Switch to force the rotation without being prompted, when Service Fabric is unhealthy. 549 | #> 550 | 551 | [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] 552 | param ( 553 | [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] 554 | [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] 555 | [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] 556 | [System.String]$NetworkController, 557 | 558 | [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] 559 | [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] 560 | [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] 561 | [System.Management.Automation.PSCredential] 562 | [System.Management.Automation.Credential()] 563 | $Credential, 564 | 565 | [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] 566 | [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] 567 | [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] 568 | [System.Management.Automation.PSCredential] 569 | [System.Management.Automation.Credential()] 570 | $NcRestCredential = [System.Management.Automation.PSCredential]::Empty, 571 | 572 | [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] 573 | [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] 574 | [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] 575 | [X509Certificate]$NcRestCertificate, 576 | 577 | [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] 578 | [System.String]$CertPath, 579 | 580 | [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] 581 | [Switch]$GenerateCertificate, 582 | 583 | [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] 584 | [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] 585 | [System.Security.SecureString]$CertPassword, 586 | 587 | [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] 588 | [datetime]$NotAfter = (Get-Date).AddYears(3), 589 | 590 | [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] 591 | [hashtable]$CertRotateConfig, 592 | 593 | [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] 594 | [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] 595 | [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] 596 | [switch]$Force 597 | ) 598 | 599 | # these are not yet supported and will take a bit more time to implement as it touches on core framework for rotate functionality 600 | # however majority of the environments impacted are using sdnexpress which leverage self-signed certificates. 601 | if ($CertRotateConfig -or $CertPath) { 602 | "This feature is not yet supported and is under development. Please use -GenerateCertificate or reference {0} for manual steps." ` 603 | -f 'https://learn.microsoft.com/en-us/azure-stack/hci/manage/update-network-controller-certificates?tabs=manual-renewal' | Trace-Output -Level:Warning 604 | return 605 | } 606 | 607 | # ensure that the module is running as local administrator 608 | Confirm-IsAdmin 609 | 610 | $array = @() 611 | $ncRestParams = @{ 612 | NcUri = $null 613 | } 614 | $putRestParams = @{ 615 | Body = $null 616 | Content = "application/json; charset=UTF-8" 617 | Headers = @{"Accept"="application/json"} 618 | Method = 'Put' 619 | Uri = $null 620 | UseBasicParsing = $true 621 | } 622 | $confirmStateParams = @{ 623 | TimeoutInSec = 600 624 | UseBasicParsing = $true 625 | } 626 | 627 | if ($PSBoundParameters.ContainsKey('NcRestCertificate')) { 628 | $restCredParam = @{ NcRestCertificate = $NcRestCertificate } 629 | $ncRestParams.Add('NcRestCertificate', $NcRestCertificate) 630 | $putRestParams.Add('Certificate', $NcRestCertificate) 631 | } 632 | else { 633 | $restCredParam = @{ NcRestCredential = $NcRestCredential } 634 | $ncRestParams.Add('NcRestCredential', $NcRestCredential) 635 | $putRestParams.Add('Credential', $NcRestCredential) 636 | } 637 | $confirmStateParams += $restCredParam 638 | 639 | try { 640 | "Starting certificate rotation" | Trace-Output 641 | "Retrieving current SDN environment details" | Trace-Output 642 | 643 | if ([String]::IsNullOrEmpty($CertPath)) { 644 | [System.String]$CertPath = "$(Get-WorkingDirectory)\MuxCert_{0}" -f (Get-FormattedDateTimeUTC) 645 | 646 | if (-NOT (Test-Path -Path $CertPath -PathType Container)) { 647 | $null = New-Item -Path $CertPath -ItemType Directory -Force 648 | } 649 | } 650 | 651 | [System.IO.FileSystemInfo]$CertPath = Get-Item -Path $CertPath -ErrorAction Stop 652 | $sdnFabricDetails = Get-SdnInfrastructureInfo -NetworkController $NetworkController -Credential $Credential @restCredParam -ErrorAction Stop 653 | if ($Global:SdnDiagnostics.EnvironmentInfo.ClusterConfigType -ine 'ServiceFabric') { 654 | throw New-Object System.NotSupportedException("This function is only supported on Service Fabric clusters.") 655 | } 656 | 657 | $ncRestParams.NcUri = $sdnFabricDetails.NcUrl 658 | $loadBalancerMuxes = Get-SdnLoadBalancerMux @ncRestParams -ErrorAction Stop 659 | 660 | # before we proceed with anything else, we want to make sure that all the Network Controllers and MUXes within the SDN fabric are running the current version 661 | Install-SdnDiagnostics -ComputerName $sdnFabricDetails.NetworkController -ErrorAction Stop 662 | Install-SdnDiagnostics -ComputerName $sdnFabricDetails.LoadBalancerMux -ErrorAction Stop 663 | 664 | ##################################### 665 | # 666 | # Create Certificate (Optional) 667 | # 668 | ##################################### 669 | 670 | if ($PSCmdlet.ParameterSetName -ieq 'GenerateCertificate') { 671 | "== STAGE: CREATE SELF SIGNED CERTIFICATES ==" | Trace-Output 672 | 673 | # retrieve the corresponding virtualserver reference for each loadbalancermux 674 | # and invoke remote operation to the mux to generate the self-signed certificate that matches the managementAddress for x509 credentials 675 | foreach ($muxResource in $loadBalancerMuxes) { 676 | $virtualServer = Get-SdnResource @ncRestParams -ResourceRef $muxResource.properties.virtualServer.resourceRef 677 | $virtualServerConnection = $virtualServer.properties.connections | Where-Object { $_.credentialType -ieq "X509Certificate" -or $_.credentialType -ieq "X509CertificateSubjectName" } 678 | $managementAddress = $virtualServerConnection.managementAddresses[0] 679 | 680 | $muxCert = Invoke-PSRemoteCommand -ComputerName $managementAddress -Credential $Credential -ScriptBlock { 681 | param( 682 | [Parameter(Position = 0)][DateTime]$param1, 683 | [Parameter(Position = 1)][PSCredential]$param2, 684 | [Parameter(Position = 2)][String]$param3, 685 | [Parameter(Position = 3)][System.Object]$param4 686 | ) 687 | 688 | New-SdnMuxCertificate -NotAfter $param1 -Credential $param2 -Path $param3 -FabricDetails $param4 689 | } -ArgumentList @($NotAfter, $Credential, $CertPath.FullName, $sdnFabricDetails) 690 | 691 | $array += [PSCustomObject]@{ 692 | ManagementAddress = $managementAddress 693 | ResourceRef = $virtualServer.resourceRef 694 | Certificate = $muxCert.Certificate 695 | } 696 | } 697 | } 698 | 699 | # loop through all the objects to perform PUT operation against the virtualServer resource 700 | # to update the base64 encoding for the certificate that NC should use when communicating with the virtualServer resource 701 | foreach ($obj in $array) { 702 | "Updating certificate information for {0}" -f $obj.ResourceRef | Trace-Output 703 | $virtualServer = Get-SdnResource @ncRestParams -ResourceRef $obj.ResourceRef 704 | $encoding = [System.Convert]::ToBase64String($obj.Certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)) 705 | 706 | if ($virtualServer.properties.certificate) { 707 | $virtualServer.properties.certificate = $encoding 708 | } 709 | else { 710 | # in instances where the certificate property does not exist, we will need to add it 711 | # this typically will occur if converting from CA issued certificate to self-signed certificate 712 | $virtualServer.properties | Add-Member -MemberType NoteProperty -Name 'certificate' -Value $encoding -Force 713 | } 714 | $putRestParams.Body = ($virtualServer | ConvertTo-Json -Depth 100) 715 | 716 | $endpoint = Get-SdnApiEndpoint -NcUri $sdnFabricDetails.NcUrl -ResourceRef $virtualServer.resourceRef 717 | $putRestParams.Uri = $endpoint 718 | 719 | $null = Invoke-RestMethodWithRetry @putRestParams 720 | if (-NOT (Confirm-ProvisioningStateSucceeded -NcUri $putRestParams.Uri @confirmStateParams)) { 721 | throw New-Object System.Exception("ProvisioningState is not succeeded") 722 | } 723 | else { 724 | "Successfully updated the certificate information for {0}" -f $obj.ResourceRef | Trace-Output 725 | } 726 | 727 | # after we have generated the certificates and updated the servers to use the new certificate 728 | # we will want to go and locate certificates that may conflict with the new certificate 729 | "Checking certificates on {0} that match {1}" -f $obj.managementAddress, $obj.Certificate.Subject | Trace-Output 730 | $certsToExamine = Invoke-PSRemoteCommand -ComputerName $obj.managementAddress -Credential $Credential -ScriptBlock { 731 | param([Parameter(Mandatory = $true)]$param1) 732 | $certs = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $param1.Subject 733 | if ($certs.Count -ge 2) { 734 | $certToRemove = $certs | Where-Object {$_.Thumbprint -ine $param1.Thumbprint} 735 | 736 | return $certToRemove 737 | } 738 | } -ArgumentList $obj.Certificate 739 | 740 | if ($certsToExamine) { 741 | "`nMultiple certificates detected for Subject: {0}. Examine the certificates and cleanup if no longer needed." -f $obj.Certificate.Subject | Trace-Output -Level:Warning 742 | foreach ($cert in $certsToExamine) { 743 | "`t[{0}] Thumbprint: {1}" -f $cert.PSComputerName, $cert.Thumbprint | Trace-Output -Level:Warning 744 | } 745 | 746 | Write-Host "" # insert empty line for better readability 747 | } 748 | 749 | # restart the slb mux service on the mux 750 | $null = Invoke-PSRemoteCommand -ComputerName $obj.managementAddress -Credential $Credential -ScriptBlock { 751 | Restart-Service -Name SlbMux -Force 752 | } 753 | } 754 | 755 | "Certificate rotation for Load Balancer Muxes has completed" | Trace-Output -Level:Success 756 | } 757 | catch { 758 | $_ | Trace-Exception 759 | $_ | Write-Error 760 | } 761 | } 762 | 763 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.NetworkController.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Name = "NetworkController" 6 | WindowsFeature = @( 7 | "NetworkController" 8 | ) 9 | RequiredModules = @() 10 | Properties = @{ 11 | apiResources = @{ 12 | AccessControlLists = @{ 13 | uri = "accessControlLists" 14 | minVersion = "v1" 15 | operationId = $false 16 | includeInResourceDump = $true 17 | } 18 | AuditingSettingsConfig = @{ 19 | uri = "auditingSettings/configuration" 20 | minVersion = "v3" 21 | operationId = $false 22 | includeInResourceDump = $true 23 | } 24 | Credentials = @{ 25 | uri = "credentials" 26 | minVersion = "v1" 27 | operationId = $false 28 | includeInResourceDump = $true 29 | } 30 | Discovery = @{ 31 | uri = "discovery" 32 | minVersion = "" 33 | operationId = $false 34 | includeInResourceDump = $true 35 | } 36 | GatewayPools = @{ 37 | uri = "gatewayPools" 38 | minVersion = "v1" 39 | operationId = $false 40 | includeInResourceDump = $true 41 | } 42 | Gateways = @{ 43 | uri = "gateways" 44 | minVersion = "v1" 45 | operationId = $false 46 | includeInResourceDump = $true 47 | } 48 | IDNSServerConfig = @{ 49 | uri = "iDNSServer/configuration" 50 | minVersion = "v1" 51 | operationId = $false 52 | includeInResourceDump = $true 53 | } 54 | internalResourceInstances = @{ 55 | uri = "internalResourceInstances" 56 | minVersion = "v1" 57 | operationId = $false 58 | includeInResourceDump = $true 59 | } 60 | LearnedIPAddresses = @{ 61 | uri = "learnedIPAddresses" 62 | minVersion = "v5" 63 | operationId = $false 64 | includeInResourceDump = $true 65 | } 66 | LoadBalancerManagerConfig = @{ 67 | uri = "loadBalancerManager/config" 68 | minVersion = "v1" 69 | operationId = $false 70 | includeInResourceDump = $true 71 | } 72 | LoadBalancerMuxes = @{ 73 | uri = "loadBalancerMuxes" 74 | minVersion = "v1" 75 | operationId = $false 76 | includeInResourceDump = $true 77 | } 78 | LoadBalancers = @{ 79 | uri = "loadBalancers" 80 | minVersion = "v1" 81 | operationId = $false 82 | includeInResourceDump = $true 83 | } 84 | LogicalNetworks = @{ 85 | uri = "logicalNetworks" 86 | minVersion = "v1" 87 | operationId = $false 88 | includeInResourceDump = $true 89 | } 90 | MacPools = @{ 91 | uri = "macPools" 92 | minVersion = "v1" 93 | operationId = $false 94 | includeInResourceDump = $true 95 | } 96 | NetworkControllerBackup = @{ 97 | uri = "networkControllerBackup" 98 | minVersion = "v1" 99 | operationId = $false 100 | includeInResourceDump = $true 101 | } 102 | NetworkControllerRestore = @{ 103 | uri = "networkControllerRestore" 104 | minVersion = "v1" 105 | operationId = $false 106 | includeInResourceDump = $true 107 | } 108 | NetworkControllerStatistics = @{ 109 | uri = "monitoring/networkControllerStatistics" 110 | minVersion = "v1" 111 | operationId = $false 112 | includeInResourceDump = $true 113 | } 114 | NetworkInterfaces = @{ 115 | uri = "networkInterfaces" 116 | minVersion = "v1" 117 | operationId = $false 118 | includeInResourceDump = $true 119 | } 120 | Operations = @{ 121 | uri = "operations" 122 | minVersion = "v1" 123 | operationId = $false 124 | includeInResourceDump = $false 125 | } 126 | OperationResults = @{ 127 | uri = "operationResults" 128 | minVersion = "v1" 129 | operationId = $true 130 | includeInResourceDump = $false 131 | } 132 | PublicIPAddresses = @{ 133 | uri = "publicIPAddresses" 134 | minVersion = "v1" 135 | operationId = $false 136 | includeInResourceDump = $true 137 | } 138 | SecurityTags = @{ 139 | uri = "securityTags" 140 | minVersion = "v5" 141 | operationId = $false 142 | includeInResourceDump = $true 143 | } 144 | Servers = @{ 145 | uri = "Servers" 146 | minVersion = "v1" 147 | operationId = $false 148 | includeInResourceDump = $true 149 | } 150 | ServiceInsertions = @{ 151 | uri = "serviceInsertions" 152 | minVersion = "v1" 153 | operationId = $false 154 | includeInResourceDump = $true 155 | } 156 | SlbState = @{ 157 | uri = "diagnostics/slbState" 158 | minVersion = "v1" 159 | operationId = $false 160 | includeInResourceDump = $false 161 | } 162 | SlbStateResults = @{ 163 | uri = "diagnostics/slbStateResults" 164 | minVersion = "v1" 165 | operationId = $true 166 | includeInResourceDump = $false 167 | } 168 | RouteTables = @{ 169 | uri = "routeTables" 170 | minVersion = "v1" 171 | operationId = $false 172 | includeInResourceDump = $true 173 | } 174 | VirtualGateways = @{ 175 | uri = "virtualGateways" 176 | minVersion = "v1" 177 | operationId = $false 178 | includeInResourceDump = $true 179 | } 180 | VirtualNetworkManagerConfig = @{ 181 | uri = "virtualNetworkManager/configuration" 182 | minVersion = "v1" 183 | operationId = $false 184 | includeInResourceDump = $true 185 | } 186 | VirtualNetworks = @{ 187 | uri = "virtualNetworks" 188 | minVersion = "v1" 189 | operationId = $false 190 | includeInResourceDump = $true 191 | } 192 | VirtualServers = @{ 193 | uri = "virtualServers" 194 | minVersion = "v1" 195 | operationId = $false 196 | includeInResourceDump = $true 197 | } 198 | VirtualSwitchManagerConfig = @{ 199 | uri = "virtualSwitchManager/configuration" 200 | minVersion = "v1" 201 | operationId = $false 202 | includeInResourceDump = $true 203 | } 204 | } 205 | CommonPaths = @{} 206 | EventLogProviders = @( 207 | "Microsoft-Windows-NetworkController-FirewallService*", 208 | "Microsoft-Windows-NetworkController-Framework*", 209 | "Microsoft-Windows-NetworkController-GatewayManager*", 210 | "Microsoft-Windows-NetworkController-SDNAPIEvents*", 211 | "Microsoft-Windows-NetworkController-SDNBREvents*", 212 | "Microsoft-Windows-NetworkController-SDNFNM*", 213 | "Microsoft-Windows-NetworkController-SDNHelper*", 214 | "Microsoft-Windows-NetworkController-SDNSlbManager*", 215 | "Microsoft-Windows-NetworkController-ServiceInsertion*", 216 | "Microsoft-Windows-NetworkController-Update*", 217 | "Microsoft-Windows-NetworkController-VSwitchService*" 218 | ) 219 | NetControllerStatePath = "C:\Windows\Tracing\SDNDiagnostics\NetworkControllerState" 220 | RegKeyPaths = @( 221 | "HKLM:\Software\Microsoft\NetworkController" 222 | "HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent" 223 | "HKLM:\SYSTEM\CurrentControlSet\Services\Network Controller" 224 | ) 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.NetworkController.FC.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Name = "NetworkController_FC" 6 | WindowsFeature = @( 7 | "NetworkController" 8 | ) 9 | RequiredModules = @() 10 | Properties = @{ 11 | CommonPaths = @{} 12 | EventLogProviders = @( 13 | 'NetworkControllerFc' 14 | ) 15 | RegKeyPaths = @( 16 | 'HKLM:\Cluster\NetworkController' 17 | ) 18 | Services = @{ 19 | SDNApiService = @{ 20 | Properties = @{ 21 | DisplayName = "SDNApiService" 22 | OwnerGroupName = "ApiService" 23 | } 24 | } 25 | SDNControllerService = @{ 26 | Properties = @{ 27 | DisplayName = "SDNControllerService" 28 | OwnerGroupName = "ControllerService" 29 | } 30 | } 31 | SDNFirewallService = @{ 32 | Properties = @{ 33 | DisplayName = "SDNFirewallService" 34 | OwnerGroupName = "FirewallService" 35 | } 36 | } 37 | SDNFnmService = @{ 38 | Properties = @{ 39 | DisplayName = "SDNFnmService" 40 | OwnerGroupName = "FnmService" 41 | } 42 | } 43 | SDNGatewayManager = @{ 44 | Properties = @{ 45 | DisplayName = "SDNGatewayManager" 46 | OwnerGroupName = "GatewayManager" 47 | } 48 | } 49 | SDNHelperService = @{ 50 | Properties = @{ 51 | DisplayName = "SDNHelperService" 52 | OwnerGroupName = "" 53 | } 54 | } 55 | SDNServiceInsertion = @{ 56 | Properties = @{ 57 | DisplayName = "SDNServiceInsertion" 58 | OwnerGroupName = "ServiceInsertion" 59 | } 60 | } 61 | SDNSlbManagerService = @{ 62 | Properties = @{ 63 | DisplayName = "SDNSlbManagerService" 64 | OwnerGroupName = "SlbManagerService" 65 | } 66 | } 67 | SDNVSwitchService = @{ 68 | Properties = @{ 69 | DisplayName = "SDNVSwitchService" 70 | OwnerGroupName = "VSwitchService" 71 | } 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.NetworkController.FC.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Import-Module $PSScriptRoot\SdnDiag.Common.psm1 5 | Import-Module $PSScriptRoot\SdnDiag.Utilities.psm1 6 | 7 | # create local variable to store configuration data 8 | $configurationData = Import-PowerShellDataFile -Path $PSScriptRoot\SdnDiag.NetworkController.FC.Config.psd1 9 | New-Variable -Name 'SdnDiagnostics_NC_FC' -Scope 'Script' -Force -Value @{ 10 | Config = $configurationData 11 | } 12 | 13 | ########################## 14 | #### CLASSES & ENUMS ##### 15 | ########################## 16 | 17 | ########################## 18 | #### ARG COMPLETERS ###### 19 | ########################## 20 | 21 | ########################## 22 | ####### FUNCTIONS ######## 23 | ########################## 24 | 25 | function Get-NetworkControllerFCConfigState { 26 | <# 27 | .SYNOPSIS 28 | Outputs a set of configuration state files for the network controller role. 29 | .PARAMETER OutputDirectory 30 | Specifies a specific path and folder in which to save the files. 31 | .EXAMPLE 32 | PS> Get-NetworkControllerFCConfigState -OutputDirectory "C:\Temp\CSS_SDN" 33 | #> 34 | 35 | [CmdletBinding()] 36 | param ( 37 | [Parameter(Mandatory = $true)] 38 | [System.IO.FileInfo]$OutputDirectory 39 | ) 40 | 41 | $currentErrorActionPreference = $ErrorActionPreference 42 | $ProgressPreference = 'SilentlyContinue' 43 | $ErrorActionPreference = 'SilentlyContinue' 44 | [string]$outDir = Join-Path -Path $OutputDirectory.FullName -ChildPath "ConfigState/NetworkController" 45 | 46 | try { 47 | $config = Get-SdnModuleConfiguration -Role 'NetworkController_FC' 48 | "Collect configuration state details for role {0}" -f $config.Name | Trace-Output 49 | if (-NOT (Initialize-DataCollection -Role $config.Name -FilePath $outDir -MinimumMB 10)) { 50 | "Unable to initialize environment for data collection for {0}" -f $config.Name | Trace-Output -Level:Error 51 | return 52 | } 53 | 54 | [string]$regDir = Join-Path -Path $outDir -ChildPath "Registry" 55 | Export-RegistryKeyConfigDetails -Path $config.properties.regKeyPaths -OutputDirectory $regDir 56 | 57 | Get-Cluster | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 58 | Get-ClusterFaultDomain | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 59 | Get-ClusterNode | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 60 | Get-ClusterGroup | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 61 | Get-ClusterNetwork | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 62 | Get-ClusterNetworkInterface | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 63 | Get-ClusterResource | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 64 | Get-ClusterResourceType | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 65 | Get-ClusterSharedVolume | Export-ObjectToFile -FilePath $outDir -FileType txt -Format List 66 | } 67 | catch { 68 | $_ | Trace-Exception 69 | $_ | Write-Error 70 | } 71 | 72 | $ProgressPreference = 'Continue' 73 | $ErrorActionPreference = $currentErrorActionPreference 74 | } 75 | 76 | function Get-SdnClusterName { 77 | [CmdletBinding()] 78 | param ( 79 | [Parameter(Mandatory = $true)] 80 | [System.String]$NetworkController, 81 | 82 | [Parameter(Mandatory = $false)] 83 | [System.Management.Automation.PSCredential] 84 | [System.Management.Automation.Credential()] 85 | $Credential = [System.Management.Automation.PSCredential]::Empty 86 | ) 87 | 88 | if (Test-ComputerNameIsLocal -ComputerName $NetworkController) { 89 | $clusterName = Get-Cluster | Select-Object -ExpandProperty Name 90 | } 91 | else { 92 | if ($null -ieq $Credential -or $Credential -eq [System.Management.Automation.PSCredential]::Empty) { 93 | $clusterName = Invoke-PSRemoteCommand -ComputerName $NetworkController -ScriptBlock { Get-Cluster } | Select-Object -ExpandProperty Name 94 | } 95 | else { 96 | $clusterName = Invoke-PSRemoteCommand -ComputerName $NetworkController -ScriptBlock { Get-Cluster } -Credential $Credential | Select-Object -ExpandProperty Name 97 | } 98 | } 99 | 100 | "Cluster Name: $clusterName" | Trace-Output -Level:Verbose 101 | return $clusterName 102 | } 103 | 104 | function Get-SdnNetworkControllerFC { 105 | <# 106 | .SYNOPSIS 107 | Gets network controller application settings from the network controller node leveraging Failover Cluster. 108 | .PARAMETER NetworkController 109 | Specifies the name or IP address of the network controller node on which this cmdlet operates. The parameter is optional if running on network controller node. 110 | .PARAMETER Credential 111 | Specifies a user account that has permission to perform this action. The default is the current user. 112 | .EXAMPLE 113 | PS> Get-SdnNetworkControllerFC 114 | .EXAMPLE 115 | PS> Get-SdnNetworkControllerFC -NetworkController 'NC01' -Credential (Get-Credential) 116 | #> 117 | 118 | [CmdletBinding()] 119 | param ( 120 | [Parameter(Mandatory = $false)] 121 | [System.String]$NetworkController = $env:COMPUTERNAME, 122 | 123 | [Parameter(Mandatory = $false)] 124 | [System.Management.Automation.PSCredential] 125 | [System.Management.Automation.Credential()] 126 | $Credential = [System.Management.Automation.PSCredential]::Empty 127 | ) 128 | 129 | try { 130 | if (Test-ComputerNameIsLocal -ComputerName $NetworkController) { 131 | Confirm-IsNetworkController 132 | $result = Get-NetworkControllerOnFailoverCluster 133 | } 134 | else { 135 | $result = Invoke-PSRemoteCommand -ComputerName $NetworkController -ScriptBlock { Get-NetworkControllerOnFailoverCluster } -Credential $Credential 136 | } 137 | 138 | return $result 139 | } 140 | catch { 141 | $_ | Trace-Exception 142 | $_ | Write-Error 143 | } 144 | } 145 | 146 | function Get-SdnNetworkControllerFCClusterInfo { 147 | <# 148 | .SYNOPSIS 149 | Gather the Network Controller cluster wide info from one of the Network Controller 150 | .PARAMETER NetworkController 151 | Specifies the name of the network controller node on which this cmdlet operates. 152 | .PARAMETER Credential 153 | Specifies a user account that has permission to perform this action. The default is the current user. 154 | .PARAMETER OutputDirectory 155 | Directory location to save results. It will create a new sub-folder called NetworkControllerClusterInfo_FC that the files will be saved to 156 | .EXAMPLE 157 | PS> Get-SdnNetworkControllerFCClusterInfo 158 | .EXAMPLE 159 | PS> Get-SdnNetworkControllerFCClusterInfo -NetworkController 'NC01' -Credential (Get-Credential) 160 | #> 161 | 162 | [CmdletBinding()] 163 | param ( 164 | [Parameter(Mandatory = $true)] 165 | [System.String]$NetworkController, 166 | 167 | [Parameter(Mandatory = $false)] 168 | [System.Management.Automation.PSCredential] 169 | [System.Management.Automation.Credential()] 170 | $Credential = [System.Management.Automation.PSCredential]::Empty, 171 | 172 | [Parameter(Mandatory = $true)] 173 | [System.IO.FileInfo]$OutputDirectory 174 | ) 175 | 176 | $currentErrorActionPreference = $ErrorActionPreference 177 | $ProgressPreference = 'SilentlyContinue' 178 | $ErrorActionPreference = 'Ignore' 179 | 180 | try { 181 | $outputDir = Join-Path -Path $OutputDirectory.FullName -ChildPath 'NetworkControllerClusterInfo_FC' 182 | if (!(Test-Path -Path $outputDir -PathType Container)) { 183 | $null = New-Item -Path $outputDir -ItemType Directory -Force 184 | } 185 | 186 | $clusterName = $Global:SdnDiagnostics.EnvironmentInfo.FailoverClusterConfig.Name 187 | if ($null -ieq $clusterName) { 188 | $clusterName = Get-SdnClusterName -NetworkController $NetworkController -Credential $Credential -ErrorAction Stop 189 | } 190 | 191 | Get-Cluster -Name $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 192 | Get-ClusterFaultDomain -CimSession $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 193 | Get-ClusterNode -Cluster $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 194 | Get-ClusterGroup -Cluster $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 195 | Get-ClusterNetwork -Cluster $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 196 | Get-ClusterNetworkInterface -Cluster $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 197 | Get-ClusterResource -Cluster $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 198 | Get-ClusterResourceType -Cluster $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 199 | Get-ClusterSharedVolume -Cluster $clusterName | Export-ObjectToFile -FilePath $outputDir -FileType txt -Format List 200 | } 201 | catch { 202 | $_ | Trace-Exception 203 | $_ | Write-Error 204 | } 205 | 206 | $ProgressPreference = 'Continue' 207 | $ErrorActionPreference = $currentErrorActionPreference 208 | } 209 | 210 | function Get-SdnNetworkControllerFCNode { 211 | <# 212 | .SYNOPSIS 213 | Returns a list of servers from network controller. 214 | .PARAMETER Name 215 | Specifies the friendly name of the node for the network controller. If not provided, settings are retrieved for all nodes in the deployment. 216 | .PARAMETER NetworkController 217 | Specifies the name or IP address of the network controller node on which this cmdlet operates. The parameter is optional if running on network controller node. 218 | .PARAMETER Credential 219 | Specifies a user account that has permission to perform this action. The default is the current user. 220 | .EXAMPLE 221 | PS> Get-SdnNetworkControllerFCNode 222 | .EXAMPLE 223 | PS> Get-SdnNetworkControllerFCNode -NetworkController 'NC01' -Credential (Get-Credential) 224 | #> 225 | 226 | [CmdletBinding()] 227 | param ( 228 | [Parameter(Mandatory = $false)] 229 | [System.String]$Name, 230 | 231 | [Parameter(Mandatory = $false)] 232 | [System.String]$NetworkController = $env:COMPUTERNAME, 233 | 234 | [Parameter(Mandatory = $false)] 235 | [System.Management.Automation.PSCredential] 236 | [System.Management.Automation.Credential()] 237 | $Credential = [System.Management.Automation.PSCredential]::Empty, 238 | 239 | [Parameter(Mandatory = $false)] 240 | [switch]$ServerNameOnly 241 | ) 242 | 243 | $params = @{ 244 | NetworkController = $NetworkController 245 | Credential = $Credential 246 | } 247 | if ($Name) { 248 | $params.Add('Name', $Name) 249 | } 250 | 251 | $sb = { 252 | param([String]$param1) 253 | # native cmdlet to get network controller node information is case sensitive 254 | # so we need to get all nodes and then filter based on the name 255 | $ncNodes = Get-ClusterNode -ErrorAction Stop 256 | if (![string]::IsNullOrEmpty($param1)) { 257 | return ($ncNodes | Where-Object {$_.Name -ieq $param1}) 258 | } 259 | else { 260 | return $ncNodes 261 | } 262 | } 263 | 264 | if (Test-ComputerNameIsLocal -ComputerName $NetworkController) { 265 | Confirm-IsNetworkController 266 | } 267 | 268 | try { 269 | if (Test-ComputerNameIsLocal -ComputerName $NetworkController) { 270 | $result = Invoke-Command -ScriptBlock $sb -ArgumentList @($Name) -ErrorAction Stop 271 | } 272 | else { 273 | $result = Invoke-PSRemoteCommand -ComputerName $NetworkController -Credential $Credential -ScriptBlock $sb -ArgumentList @($Name) -ErrorAction Stop 274 | } 275 | 276 | # in this scenario if the results returned we will parse the objects returned and generate warning to user if cluster node is not up 277 | foreach($obj in $result){ 278 | if($obj.State -ine 'Up'){ 279 | "{0} is reporting state {1}" -f $obj.Name, $obj.State | Trace-Output -Level:Warning 280 | } 281 | } 282 | 283 | if($ServerNameOnly){ 284 | return [System.Array]$result.Name 285 | } 286 | else { 287 | return $result 288 | } 289 | } 290 | catch { 291 | $_ | Trace-Exception 292 | $_ | Write-Error 293 | } 294 | } 295 | 296 | function Confirm-IsFailoverClusterNC { 297 | $service = Get-Service -Name 'SDNApiService' -ErrorAction Ignore 298 | if ($service) { 299 | return $true 300 | } 301 | 302 | return $false 303 | } 304 | 305 | function Get-SdnClusterLog { 306 | [CmdletBinding()] 307 | param( 308 | [Parameter(Position = 0)] 309 | [System.IO.FileInfo]$OutputDirectory = (Get-WorkingDirectory) 310 | ) 311 | 312 | # The 3>$null 4>$null sends warning and error to null 313 | # typically Get-ClusterLog does not like remote powershell operations and generates warnings/errors 314 | $clusterLogFiles = Get-ClusterLog -Destination $OutputDirectory.FullName 2>$null 3>$null 315 | 316 | # if we have cluster log files, we will zip them up to preserve disk space 317 | if ($clusterLogFiles) { 318 | $clusterLogFiles | ForEach-Object { 319 | $zipFilePath = Join-Path -Path $OutputDirectory.FullName -ChildPath ($_.Name + ".zip") 320 | Compress-Archive -Path $_.FullName -DestinationPath $zipFilePath -Force -ErrorAction Stop 321 | 322 | # if the file was successfully zipped, we can remove the original file 323 | if (Get-Item -Path $zipFilePath -ErrorAction Ignore) { 324 | Remove-Item -Path $_.FullName -Force -ErrorAction Ignore 325 | } 326 | } 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.NetworkController.SF.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Name = "NetworkController_SF" 6 | WindowsFeature = @( 7 | "NetworkController" 8 | ) 9 | RequiredModules = @() 10 | Properties = @{ 11 | CommonPaths = @{ 12 | serviceFabricLogDirectory = @( 13 | "C:\ProgramData\Microsoft\Service Fabric\log\Traces" 14 | "C:\ProgramData\Microsoft\Service Fabric\log\OperationalTraces" 15 | "C:\ProgramData\Microsoft\Service Fabric\log\QueryTraces" 16 | "C:\ProgramData\Microsoft\Service Fabric\log\CrashDumps" 17 | "C:\ProgramData\Microsoft\Service Fabric\log\PerformanceCounters_WinFabPerfCtrFolder" 18 | ) 19 | } 20 | EventLogProviders = @( 21 | "Microsoft-ServiceFabric*" 22 | ) 23 | RegKeyPaths = @() 24 | Services = @{ 25 | FabricHostSvc = @{ 26 | Properties = @{ 27 | DisplayName = "Service Fabric Host Service" 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.Server.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Name = "Server" 6 | WindowsFeature = @( 7 | "Hyper-V" 8 | ) 9 | RequiredModules = @( 10 | "Hyper-V", 11 | "HnvDiagnostics" 12 | ) 13 | Properties = @{ 14 | CommonPaths = @{} 15 | EventLogProviders = @( 16 | "Microsoft-Windows-Hyper-V*" 17 | "Microsoft-Windows-NetworkController-NcHostAgent-Admin" 18 | "Microsoft-Windows-Networking-NetworkAtc*" 19 | ) 20 | RegKeyPaths = @( 21 | "HKLM:\Software\Microsoft\NetworkController" 22 | "HKLM:\SYSTEM\CurrentControlSet\Services\DnsProxy" 23 | "HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent" 24 | "HKLM:\SYSTEM\CurrentControlSet\Services\SlbHostAgent" 25 | ) 26 | Services = @{ 27 | NcHostAgent = @{ 28 | Properties = @{ 29 | DisplayName = "NC Host Agent" 30 | } 31 | } 32 | SlbHostAgent = @{ 33 | Properties = @{ 34 | DisplayName = "Software Load Balancer Host Agent" 35 | } 36 | } 37 | } 38 | EtwTraceProviders = @{ 39 | nchostagent = @{ 40 | isOptional = $false 41 | Providers = @( 42 | "{28F7FB0F-EAB3-4960-9693-9289CA768DEA}" 43 | "{A6527853-5B2B-46E5-9D77-A4486E012E73}" 44 | "{dbc217a8-018f-4d8e-a849-acea31bc93f9}" 45 | "{41DC7652-AAF6-4428-BBBB-CFBDA322F9F3}" 46 | "{F2605199-8A9B-4EBD-B593-72F32DEEC058}" 47 | ) 48 | Level = 4 49 | Keywords = "0xFFFFFFFFFFFFFFFF" 50 | } 51 | nvsp = @{ 52 | isOptional = $false 53 | Providers = @( 54 | "{1F387CBC-6818-4530-9DB6-5F1058CD7E86}" 55 | "{6C28C7E5-331B-4437-9C69-5352A2F7F296}" 56 | ) 57 | Level = 4 58 | Keywords = "0xFFFFFFFFFFFFFFFF" 59 | } 60 | slbhostagent = @{ 61 | isOptional = $false 62 | Providers = @( 63 | "{2380c5ee-ab89-4d14-b2e6-142200cb703c}" 64 | ) 65 | Level = 4 66 | Keywords = "0xFFFFFFFFFFFFFFFF" 67 | } 68 | vfpext = @{ 69 | isOptional = $false 70 | Providers = @( 71 | "{9F2660EA-CFE7-428F-9850-AECA612619B0}" 72 | "{67DC0D66-3695-47C0-9642-33F76F7BD7AD}" 73 | ) 74 | Level = 4 75 | Keywords = "0xFFFFFFFFFFFFFFFF" 76 | } 77 | } 78 | } 79 | ResourceName = "Servers" 80 | } 81 | -------------------------------------------------------------------------------- /src/modules/SdnDiag.Utilities.Config.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | FolderPathsAllowedForCleanup = @( 6 | "C:\Windows\Tracing\SdnDiag" 7 | "C:\Windows\Tracing\SdnDiag\*" 8 | ) 9 | DefaultModuleDirectory = "C:\Program Files\WindowsPowerShell\Modules\SdnDiagnostics" 10 | WorkingDirectory = "C:\Windows\Tracing\SdnDiag" 11 | ExportFileJsonDepth = @{ 12 | 3 = @( 13 | 'Get-NetAdapterChecksumOffload' 14 | 'Get-NetAdapterLso' 15 | 'Get-NetAdapterRdma' 16 | 'Get-NetAdapterRsc' 17 | 'Get-NetAdapterRss' 18 | 'Get-NetAdapterStatistics' 19 | 'Get-NetIPConfiguration' 20 | 'Get-SdnVfpVmSwitchPort' 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/modules/Test-SdnExpressBgp.psm1: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------- 2 | # Copyright © Microsoft Corporation. All Rights Reserved. 3 | # Microsoft Corporation (or based on where you live, one of its affiliates) licenses this sample code for your internal testing purposes only. 4 | # 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. 5 | # Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 6 | # The entire risk arising out of the use or performance of the sample code remains with you. 7 | # 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 8 | # (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) 9 | # 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. 10 | # --------------------------------------------------------------- 11 | 12 | # BGP-4 implementation 13 | 14 | # RFCs: 15 | # RFC 4271 BGP-4 16 | # RFC 3392 Capabilities Advertisement with BGP-4 17 | # RFC 2918 Route Refresh for BGP-4 18 | 19 | # Slightly implemented (extra data shouldn't break): 20 | # RFC 4760 Multiprotocol Extensions for BGP-4 21 | 22 | # TODO: 23 | # Detect RAS BGP, temporarily disable with -force. 24 | 25 | # Usage Examples: 26 | # 27 | # computer already has NIC: 28 | # Test-SdnExpressBGP -RouterIPAddress 10.10.182.3 -LocalIPAddress 10.10.182.7 -LocalASN 64628 -verbose -ComputerName sa18n22mux02 29 | # computer does not have NIC: 30 | # $h = New-SdnExpressBGPHost -computername sa18n22-2 -localipaddress "10.10.182.20" -prefixlength 26 -vlanid 11 31 | # $h | Test-SdnExpressBGP -RouterIPAddress "10.10.182.3" -LocalASN 64628 32 | # $h | Remove-SdnExpressBGPHost 33 | 34 | function GetBGPPathAttributeType { 35 | param( 36 | [int] $code 37 | ) 38 | if ($code -lt $BGP_PATH_ATTRIBUTE_TYPES.count) { 39 | return $BGP_PATH_ATTRIBUTE_TYPES[$code] 40 | } 41 | else { 42 | return "$code" 43 | } 44 | } 45 | 46 | function CapabilityCodeLookup { 47 | param( 48 | [int] $code 49 | ) 50 | switch ($code) { 51 | 0 { return "Reserved" } 52 | 1 { return "Multiprotocol Extensions for BGP-4" } 53 | 2 { return "Route Refresh Capability for BGP-4" } 54 | 3 { return "Outbound Route Filtering Capability" } 55 | 4 { return "Multiple routes to a destination capability (deprecated)" } 56 | 5 { return "Extended Next Hop Encoding" } 57 | 6 { return "BGP Extended Message" } 58 | 7 { return "BGPsec Capability" } 59 | 8 { return "Multiple Labels Capability" } 60 | 9 { return "BGP Role (TEMPORARY)" } 61 | { $_ -in 10..63 } { return "Unassigned" } 62 | 64 { return "Graceful Restart Capability" } 63 | 65 { return "Support for 4-octet AS number capability" } 64 | 66 { return "Deprecated" } 65 | 67 { return "Support for Dynamic Capability (capability specific)" } 66 | 68 { return "Multisession BGP Capability" } 67 | 69 { return "ADD-PATH Capability" } 68 | 70 { return "Enhanced Route Refresh Capability" } 69 | 71 { return "Long-Lived Graceful Restart (LLGR) Capability" } 70 | 72 { return "Routing Policy Distribution" } 71 | 73 { return "FQDN Capability" } 72 | { $_ -in 74..127 } { return "Unassigned" } 73 | 128 { return "Prestandard Route Refresh (deprecated)" } 74 | 129 { return "Prestandard Outbound Route Filtering (deprecated)" } 75 | 130 { return "Prestandard Outbound Route Filtering (deprecated)" } 76 | 131 { return "Prestandard Multisession (deprecated)" } 77 | { $_ -in 132..183 } { return "Unassigned" } 78 | 184 { return "Prestandard FQDN (deprecated)" } 79 | 185 { return "Prestandard OPERATIONAL message (deprecated)" } 80 | { $_ -in 186..238 } { return "Unassigned" } 81 | { $_ -in 239..254 } { return "Reserved for Experimental Use" } 82 | 255 { return "Reserved" } 83 | } 84 | 85 | } 86 | 87 | function GetBytes { 88 | param( 89 | [byte[]] $bytes, 90 | [int] $offset, 91 | [int] $count 92 | ) 93 | return $bytes[$offset..($offset + $count - 1)] 94 | } 95 | function GetInt32 { 96 | param( 97 | [byte[]] $bytes, 98 | [int] $offset 99 | ) 100 | return [System.Int64]($bytes[$offset] * [Math]::pow(2, 24)) + ($bytes[$offset + 1] * [Math]::pow(2, 16)) + ($bytes[$offset + 2] * [Math]::pow(2, 8)) + $bytes[$offset + 3] 101 | } 102 | 103 | function GetInt16 { 104 | param( 105 | [byte[]] $bytes, 106 | [int] $offset 107 | ) 108 | return [Int]($bytes[$offset] * 256) + $bytes[$offset + 1] 109 | } 110 | function GetInt8 { 111 | param( 112 | [byte[]] $bytes, 113 | [int] $offset 114 | ) 115 | return [Int]$bytes[$offset] 116 | } 117 | function SetInt8 { 118 | param( 119 | [byte[]] $bytes, 120 | [int] $offset, 121 | [int] $value 122 | ) 123 | $bytes[$offset] = $value 124 | return $bytes 125 | } 126 | function SetInt16 { 127 | param( 128 | [byte[]] $bytes, 129 | [int] $offset, 130 | [int] $value 131 | ) 132 | $bytes[$offset] = [byte](($value -shr 8) -band [Byte] 0xFF) 133 | $bytes[$offset + 1] = [byte]( $value -band [Byte] 0xFF) 134 | return $bytes 135 | } 136 | function SetInt32 { 137 | param( 138 | [byte[]] $bytes, 139 | [int] $offset, 140 | [int] $value 141 | ) 142 | $bytes[$offset] = $value -band 0xFF 143 | $bytes[$offset + 1] = ($value -shr 8) -band 0xFF 144 | $bytes[$offset + 2] = ($value -shr 16) -band 0xFF 145 | $bytes[$offset + 3] = ($value -shr 24) -band 0xFF 146 | return $bytes 147 | } 148 | function AddInt8 { 149 | param( 150 | [byte[]] $bytes, 151 | [int] $value 152 | ) 153 | $bytes += [byte] $value 154 | return $bytes 155 | } 156 | function AddInt16 { 157 | param( 158 | [byte[]] $bytes, 159 | [int] $value 160 | ) 161 | $bytes += [byte] (($value -shr 8) -band [byte] 0xFF) 162 | $bytes += [byte] ($value -band [byte] 0xFF) 163 | return $bytes 164 | } 165 | function AddInt32 { 166 | param( 167 | [byte[]] $bytes, 168 | [System.Int64] $value 169 | ) 170 | $bytes += [byte]($value -band [byte]0xFF) 171 | $bytes += [byte](($value -shr 8) -band [byte]0xFF) 172 | $bytes += [byte](($value -shr 16) -band [byte]0xFF) 173 | $bytes += [byte](($value -shr 24) -band [byte]0xFF) 174 | return $bytes 175 | } 176 | function Get-BGPHeader { 177 | param( 178 | [byte[]] $bytes 179 | ) 180 | $header = @{} 181 | $header.Marker = GetBytes $bytes $BGP_HEADER_MARKER_OFFSET 16 182 | $header.Length = GetInt16 $bytes $BGP_HEADER_LENGTH_OFFSET 183 | $header.Type = $BGP_TYPES[(GetInt8 $bytes $BGP_HEADER_TYPE_OFFSET)] 184 | return $header 185 | 186 | } 187 | function New-BGPOpen { 188 | 189 | [byte[]] $bytes = @() 190 | for ($i = 0; $i -lt 16; $i++) { 191 | $bytes += [byte] 0xFF 192 | } 193 | $bytes = AddInt16 $bytes 0 194 | $bytes = AddInt8 $bytes 1 #OPEN 195 | $bytes = AddInt8 $bytes 4 196 | $bytes = AddInt16 $bytes $LocalASN #64628 197 | $bytes = AddInt16 $bytes 180 198 | $bytes = AddInt32 $bytes (([IPAddress] $localIPAddress).Address) 199 | 200 | #Uncomment if no optional params: 201 | #$bytes = AddInt8 $bytes 0 202 | 203 | #opt parms - hardcoded for now to include: 204 | 205 | $bytes = AddInt8 $bytes 12 #opt params len 206 | $bytes = AddInt8 $bytes 2 #type: capability code 207 | $bytes = AddInt8 $bytes 10 #len 208 | 209 | # 1 - Multiprotocol extensions for BGP-4: 0101 210 | $bytes = AddInt8 $bytes 1 #capability code 211 | $bytes = AddInt8 $bytes 4 #len 212 | $bytes = AddInt8 $bytes 0 213 | $bytes = AddInt8 $bytes 1 214 | $bytes = AddInt8 $bytes 0 215 | $bytes = AddInt8 $bytes 1 216 | 217 | # 2 - Route Refresh Capability for BGP-4 218 | $bytes = AddInt8 $bytes 2 #capability code 219 | $bytes = AddInt8 $bytes 0 #len 220 | 221 | # 128 - Prestandard Route Refresh (deprecated) 222 | $bytes = AddInt8 $bytes 128 #capability code 223 | $bytes = AddInt8 $bytes 0 #len 224 | 225 | $bytes = SetInt16 $bytes $BGP_HEADER_LENGTH_OFFSET (29 + (GetInt8 $bytes $BGP_OPEN_OPTPARMLEN_OFFSET)) 226 | return $bytes 227 | } 228 | function New-BGPKeepalive { 229 | 230 | [byte[]] $bytes = @() 231 | for ($i = 0; $i -lt 16; $i++) { 232 | $bytes += [byte] 0xFF 233 | } 234 | $bytes = AddInt16 $bytes 19 235 | $bytes = AddInt8 $bytes 4 #KEEPALIVE 236 | 237 | return $bytes 238 | } 239 | 240 | 241 | function Get-BGPUpdate { 242 | param( 243 | [byte[]] $bytes 244 | ) 245 | $update = @{} 246 | 247 | $TotalLen = GetInt16 $bytes $BGP_HEADER_LENGTH_OFFSET 248 | 249 | $WithdrawnRoutesLen = GetInt16 $bytes $BGP_UPDATE_WITHDRAWN_OFFSET 250 | $PathAttributesLen = GetInt16 $bytes ($BGP_UPDATE_WITHDRAWN_OFFSET + $withdrawnRoutesLen + 2) 251 | $NetworkLayerLen = $TotalLen - 23 - $PathAttributesLen - $WithdrawnRoutesLen 252 | 253 | $WithdrawnRoutesStart = $BGP_UPDATE_WITHDRAWN_OFFSET 254 | $PathAttributesStart = $WithdrawnRoutesStart + 2 + $WithdrawnRoutesLen 255 | $NetworkLayerStart = $PathAttributesStart + 2 + $PathAttributesLen 256 | 257 | Write-Verbose "Total length: $TotalLen" 258 | Write-Verbose "Withdrawn routes start: $WithdrawnRoutesStart" 259 | Write-Verbose "Withdrawn routes len: $WithdrawnRoutesLen" 260 | Write-Verbose "Path Attributes start: $PathAttributesStart" 261 | Write-Verbose "Path Attributes len: $PathAttributesLen" 262 | Write-Verbose "Network Layer start: $NetworkLayerStart" 263 | Write-Verbose "Network Layer len: $NetworkLayerLen" 264 | 265 | Write-Verbose "Parsing Withdrawn Routes" 266 | $update.WithdrawnRoutes = @() 267 | $index = $WithdrawnRoutesStart + 2 268 | while ($index -lt $PathAttributesStart) { 269 | $PrefixBits = GetInt8 $bytes $index 270 | $PrefixBytes = [math]::ceiling($PrefixBits / 8) 271 | 272 | if ($PrefixBytes -gt 0) { 273 | $subnetBytes = GetBytes $bytes ($index + 1) $PrefixBytes 274 | for ($i = $PrefixBytes; $i -lt 4; $i++) { 275 | $subnetBytes += 0 276 | } 277 | $subnet = ([ipaddress] [byte[]]$subnetBytes).ipaddresstostring 278 | $update.WithdrawnRoutes += "$subnet/$prefixBits" 279 | } 280 | else { 281 | $update.WithdrawnRoutes += "0.0.0.0/0" 282 | } 283 | 284 | $index += $PrefixBytes + 1 285 | } 286 | 287 | Write-Verbose "Parsing Path Attributes" 288 | $update.PathAttributes = @() 289 | $index = $PathAttributesStart + 2 290 | while ($index -lt $NetworkLayerStart) { 291 | $PA = @{} 292 | $AttrFlags = GetInt8 $bytes ($index) 293 | $PA.Optional = [bool]($AttrFlags -band 128) 294 | $PA.Transitive = [bool]($AttrFlags -band 64) 295 | $PA.Partial = [bool]($AttrFlags -band 32) 296 | $PA.ExtendedLength = [bool]($AttrFlags -band 16) 297 | 298 | $PA.AttrType = GetBGPPathAttributeType(GetInt8 $bytes ($index + 1)) 299 | 300 | if ($PA.ExtendedLength) { 301 | $AttrLen = GetInt16 $bytes ($index + 2) 302 | $AttrLenLen = 2 303 | } 304 | else { 305 | $AttrLen = GetInt8 $bytes ($index + 2) 306 | $AttrLenLen = 1 307 | } 308 | 309 | switch ($PA.AttrType) { 310 | "ORIGIN" { 311 | $PA.Value = $BGP_PATH_ATTRIBUTE_ORIGIN_VALUE[(GetInt8 $bytes ($index + 2 + $AttrLenLen))] 312 | } 313 | "AS_PATH" { 314 | $PA.ASPath = @() 315 | $pathindex = 0 316 | while ($pathindex -lt $AttrLen) { 317 | $AttrValue = @{} 318 | $AttrValue.PathSegmentType = $BGP_PATH_ATTRIBUTE_AS_PATH_SEGMENT_TYPE[(GetInt8 $bytes ($index + $pathindex + 2 + $AttrLenLen))] 319 | $ASPaths = GetInt8 $bytes ($index + $pathindex + 4) 320 | $ASIndex = 0 321 | $AttrValue.ASes = @() 322 | while ($ASIndex -lt $ASPaths) { 323 | $AttrValue.ASes += GetInt16 $bytes ($index + $pathindex + 4 + $AttrLenLen + $ASIndex * 2) 324 | $ASIndex += 1 325 | } 326 | $PA.ASPath += $AttrValue 327 | $PathIndex += 2 + $ASPaths * 2 328 | } 329 | # 330 | #types: 1 AS_SET, 2 AS_SEQUENCE 331 | #value: set of ASes (Int16 ea) 332 | } 333 | "NEXT_HOP" { 334 | $PA.NextHop = ([ipaddress] (GetInt32 $bytes ($index + 2 + $AttrLenLen))).ipaddresstostring 335 | } 336 | { $_ -in "MULTI_EXIT_DISC", "LOCAL_PREF" } { 337 | $PA.Value = (GetInt32 $bytes ($index + 2 + $AttrLenLen)) 338 | } 339 | "ATOMIC_AGGREGATE" { 340 | #Intentionally blank, no Attr Value 341 | } 342 | "AGGREGATOR" { 343 | $PA.AS = (GetInt16 $bytes ($index + 2 + $AttrLenLen)) 344 | $PA.IPAddress = ([ipaddress] (GetInt32 $bytes ($index + 4 + $AttrLenLen))).ipaddresstostring 345 | } 346 | default { 347 | $PA.AttrValue = GetBytes $Bytes ($index + 2 + $AttrLenLen) $AttrLen 348 | } 349 | } 350 | 351 | $update.PathAttributes += $PA 352 | $index += $AttrLen + 2 + $AttrLenLen 353 | } 354 | 355 | Write-Verbose "Parsing Network Layer Reachability" 356 | 357 | $update.Prefixes = @() 358 | $index = $NetworkLayerStart 359 | 360 | while ($index -lt $TotalLen) { 361 | $PrefixBits = GetInt8 $bytes $index 362 | $PrefixBytes = [math]::ceiling($PrefixBits / 8) 363 | 364 | if ($PrefixBytes -gt 0) { 365 | $subnetBytes = GetBytes $bytes ($index + 1) $PrefixBytes 366 | for ($i = $PrefixBytes; $i -lt 4; $i++) { 367 | $subnetBytes += 0 368 | } 369 | $subnet = ([ipaddress] [byte[]]$subnetBytes).ipaddresstostring 370 | $update.Prefixes += "$subnet/$prefixBits" 371 | } 372 | else { 373 | $update.Prefixes += "0.0.0.0/0" 374 | } 375 | $Index += $PrefixBytes + 1 376 | } 377 | 378 | return $update 379 | } 380 | 381 | function Get-BGPOpen { 382 | param( 383 | [byte[]] $bytes 384 | ) 385 | $open = @{} 386 | $open.Version = GetInt8 $bytes $BGP_OPEN_VERSION_OFFSET 387 | $open.AS = GetInt16 $bytes $BGP_OPEN_AS_OFFSET 388 | $open.HoldTime = GetInt16 $bytes $BGP_OPEN_HOLDTIME_OFFSET 389 | $open.BGPID = ([ipaddress] (GetInt32 $bytes $BGP_OPEN_BGPID_OFFSET)).ipaddresstostring 390 | $OptParmLen = GetInt8 $bytes $BGP_OPEN_OPTPARMLEN_OFFSET 391 | if ($optParmLen -gt 0) { 392 | $OptParms = GetBytes $bytes $BGP_OPEN_OPTPARM_OFFSET $OptParmLen 393 | $open.OptParams = @() 394 | $index = 0 395 | 396 | while ($index -lt $OptParmLen) { 397 | $newparam = @{} 398 | $newparamType = GetInt8 $OptParms ($index) 399 | $newparam.Type = $BGP_OPEN_OPTPARAM_TYPES[$newparamType] 400 | $newparamLength = GetInt8 $OptParms ($index + 1) 401 | $ParmValue = GetBytes $OptParms ($index + 2) ($newparamLength) 402 | $newparam.values = @() 403 | if ($newparamType -eq 2) { 404 | $capindex = 0 405 | while ($capindex -lt $newparamlength) { 406 | $newcap = @{} 407 | $newcap.Code = CapabilityCodeLookup (GetInt8 $ParmValue ($capindex)) 408 | $newcapLength = GetInt8 $ParmValue ($capindex + 1) 409 | if ($newcaplength -gt 0) { 410 | $newcap.value = GetBytes $ParmValue ($capindex + 2) ($newcaplength) 411 | } 412 | $newparam.values += $newcap 413 | $capindex += $newcapLength + 2 414 | } 415 | } 416 | $open.OptParams += $newparam 417 | $index += $newparamLength + 2 418 | } 419 | } 420 | return $open 421 | } 422 | 423 | function Get-BGPNotification { 424 | param( 425 | [byte[]] $bytes 426 | ) 427 | $notification = @{} 428 | $notification.ErrorCode = $BGP_ERROR_CODES[(GetInt8 $bytes $BGP_NOTIFICATION_CODE_OFFSET)] 429 | if ((GetInt8 $bytes $BGP_NOTIFICATION_CODE_OFFSET) -eq 1) { 430 | #Message 431 | $notification.ErrorSubcode = $BGP_ERROR_SUBCODE_MESSAGE[(GetInt8 $bytes $BGP_NOTIFICATION_SUBCODE_OFFSET)] 432 | } 433 | elseif ((GetInt8 $bytes $BGP_NOTIFICATION_CODE_OFFSET) -eq 2) { 434 | #OPEN 435 | $notification.ErrorSubcode = $BGP_ERROR_SUBCODE_OPEN[(GetInt8 $bytes $BGP_NOTIFICATION_SUBCODE_OFFSET)] 436 | } 437 | elseif ((GetInt8 $bytes $BGP_NOTIFICATION_CODE_OFFSET) -eq 6) { 438 | #CEASE 439 | $notification.ErrorSubcode = $BGP_ERROR_SUBCODE_CEASE[(GetInt8 $bytes $BGP_NOTIFICATION_SUBCODE_OFFSET)] 440 | } 441 | else { 442 | $notification.ErrorSubcode = GetInt8 $bytes $BGP_NOTIFICATION_SUBCODE_OFFSET 443 | } 444 | $notification.Data = GetInt16 $bytes $BGP_NOTIFICATION_DATA_OFFSET ((GetInt16 $bytes $BGP_HEADER_LENGTH_OFFSET) - 21) 445 | 446 | 447 | return $notification 448 | } 449 | 450 | 451 | function Set-BGPState { 452 | param( 453 | [BGPState] $State 454 | ) 455 | 456 | Write-Verbose "BGP state change from $($Script:BGPState) to $State" 457 | $Script:bgpState = $State 458 | } 459 | 460 | function Get-BGPState { 461 | return $Script:bgpState 462 | } 463 | 464 | 465 | enum BGPOptParamType { 466 | Authentication 467 | Capabilities 468 | } 469 | 470 | enum BGPState { 471 | Idle 472 | Connect 473 | Active 474 | OpenSent 475 | OpenConfirm 476 | Established 477 | Custom 478 | } 479 | 480 | enum BGPOrigin { 481 | EGP 482 | IGP 483 | Incomplete 484 | } 485 | 486 | class BGPCapability { 487 | [String] $Code 488 | [byte[]] $Value 489 | [string]ToString() { 490 | return ($this.code) 491 | } 492 | } 493 | class BGPOptParam { 494 | [BGPOptParamType] $Type 495 | [BGPCapability[]] $Capabilities 496 | [string]ToString() { 497 | return ($this.type) 498 | } 499 | } 500 | 501 | class BGPPath { 502 | [string] $prefix 503 | [string] $NextHop 504 | [Int32[]] $Path 505 | [String] $LocPrf 506 | [Int32] $Metric 507 | [BGPOrigin] $Origin 508 | [string]ToString() { 509 | return ($this.prefix) 510 | } 511 | } 512 | 513 | class BGPPeer { 514 | [string] $LocalIPAddress 515 | [int32] $LocalAS 516 | [string] $RouterIPAddress 517 | [int32] $RouterAS 518 | [int16] $HoldTime 519 | [int16] $Version 520 | [string] $BGPID 521 | [BGPState] $State 522 | [BGPOptParam[]] $OptParams 523 | [BGPPath[]] $Routes 524 | } 525 | 526 | class BGPHost { 527 | [string] $ComputerName 528 | [string] $LocalIPAddress 529 | } 530 | 531 | function New-SdnExpressBGPHost { 532 | [CmdletBinding()] 533 | param( 534 | [String] $ComputerName = "localhost", 535 | [string] $SwitchName = "", 536 | [String] $LocalIPAddress, 537 | [Int32] $PrefixLength, 538 | [Int32] $VLANID = 0 539 | ) 540 | 541 | #TODO: remember gateway parameter and during test add /32 route only if needed 542 | #TODO: test for hyper-v and Hyper-v powershell PS 543 | 544 | if ([String]::IsNullorEmpty($ComputerName) ) { 545 | Write-Verbose "Running locally." 546 | $Session = @{} 547 | } 548 | else { 549 | Write-Verbose "Running on $ComputerName." 550 | $Session = @{ 551 | session = new-pssession -computername $ComputerName 552 | } 553 | } 554 | 555 | Invoke-Command @session -ArgumentList $SwitchName, $LocalIPAddress, $PrefixLength, $VLANID { 556 | param( 557 | [string] $SwitchName, 558 | [String] $LocalIPAddress, 559 | [Int32] $PrefixLength, 560 | [Int32] $VLANID 561 | ) 562 | if ([string]::IsNullOrEmpty($SwitchName)) { 563 | $vmswitch = Get-vmswitch 564 | if ($null -ieq $vmswitch) { 565 | throw "No virtual switch found." 566 | } 567 | if ($vmswitch.count -gt 1) { 568 | throw "Hyper-V host contains more than one virtual switch. Use SwitchName parameter to select a virtual switch." 569 | } 570 | $SwitchName = $vmswitch.name 571 | } 572 | 573 | Get-vmnetworkadapter -managementos -Name BGP -erroraction silentlycontinue | remove-vmnetworkadapter 574 | 575 | Add-vmnetworkadapter -ManagementOS -SwitchName $SwitchName -Name "BGP" | out-null 576 | Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdaptername "BGP" -VlanId $VLANID -Access | out-null 577 | Set-NetIPInterface -InterfaceAlias "vEthernet (BGP)" -Dhcp Disabled | out-null 578 | Set-dnsclient -InterfaceAlias "vEthernet (BGP)" -RegisterThisConnectionsAddress $False | out-null 579 | 580 | new-NetIPAddress -IPAddress $LocalIPAddress -InterfaceAlias "vEthernet (BGP)" -PrefixLength $PrefixLength | out-null 581 | 582 | } 583 | 584 | $BGPHost = [BGPHost]::New() 585 | $BGPhost.Computername = $ComputerName 586 | $BGPhost.LocalIPAddress = $LocalIPAddress 587 | return $BGPHost 588 | } 589 | 590 | function Remove-SdnExpressBGPHost { 591 | [CmdletBinding()] 592 | param( 593 | [string] $ComputerName = $null, 594 | [Parameter(Mandatory = $false, ValueFromPipeline = $true)] 595 | [BGPHost] $BGPHost = $null 596 | ) 597 | 598 | if ($BGPHost) { 599 | $computername = $BGPHost.ComputerName 600 | } 601 | 602 | if ([String]::IsNullorEmpty($Computername) ) { 603 | Write-Verbose "Running locally." 604 | $Session = @{} 605 | } 606 | else { 607 | Write-Verbose "Running on $ComputerName." 608 | $Session = @{ 609 | session = new-pssession -computername $ComputerName 610 | } 611 | } 612 | 613 | Invoke-Command @session { 614 | Get-vmnetworkadapter -managementos -Name BGP -erroraction silentlycontinue | remove-vmnetworkadapter 615 | } 616 | } 617 | 618 | 619 | function Test-SdnExpressBGP { 620 | [CmdletBinding()] 621 | param( 622 | [String] $RouterIPAddress, 623 | [String] $LocalIPAddress, 624 | [String] $LocalASN, 625 | [int32] $Wait = 3, 626 | [String] $ComputerName = "localhost", 627 | [Switch] $force, 628 | [Parameter(Mandatory = $false, ValueFromPipeline = $true)] 629 | [BGPHost] $BGPHost = $null 630 | ) 631 | 632 | if ($BGPHost) { 633 | $ComputerName = $BGPHost.ComputerName 634 | $LocalIPAddress = $BGPHost.LocalIPAddress 635 | } 636 | 637 | if ([String]::IsNullorEmpty($Computername) ) { 638 | Write-Verbose "Running locally." 639 | $Session = @{} 640 | } 641 | else { 642 | Write-Verbose "Running on $ComputerName." 643 | $Session = @{ 644 | session = new-pssession -computername $ComputerName 645 | } 646 | } 647 | 648 | $BGP_HEADER_LEN = 19 649 | $BGP_HEADER_MARKER_OFFSET = 0 650 | $BGP_HEADER_LENGTH_OFFSET = 16 651 | $BGP_HEADER_TYPE_OFFSET = 18 652 | $BGP_TYPES = @("", "OPEN", "UPDATE", "NOTIFICATION", "KEEPALIVE", "ROUTEREFRESH") 653 | 654 | $BGP_OPEN_VERSION_OFFSET = $BGP_HEADER_LEN + 0 655 | $BGP_OPEN_AS_OFFSET = $BGP_HEADER_LEN + 1 656 | $BGP_OPEN_HOLDTIME_OFFSET = $BGP_HEADER_LEN + 3 657 | $BGP_OPEN_BGPID_OFFSET = $BGP_HEADER_LEN + 5 658 | $BGP_OPEN_OPTPARMLEN_OFFSET = $BGP_HEADER_LEN + 9 659 | $BGP_OPEN_OPTPARM_OFFSET = $BGP_HEADER_LEN + 10 660 | $BGP_OPEN_OPTPARAM_TYPES = @("", "Authentication (deprecated)", "Capabilities") 661 | 662 | $BGP_ERROR_CODES = @("", "Message Header Error", "OPEN Message Error", "UPDATE Message Error", "Hold Timer Expired", "Finite State Machine Error", "Cease") 663 | $BGP_ERROR_SUBCODE_MESSAGE = @("", "Connection Not Synchronized.", "Bad Message Length.", "Bad Message Type.") 664 | $BGP_ERROR_SUBCODE_OPEN = @("", "Unsupported Version Number.", "Bad Peer AS.", "Bad BGP Identifier.", "Unsupported Optional Parameter.", "5 [Deprecated]", "Unacceptable Hold Time.") 665 | $BGP_ERROR_SUBCODE_CEASE = @("", "Maximum Number of Prefixes Reached.", "Administrative Shutdown.", "Peer De-configured.", "Administrative Reset.", "Connection Rejected.", "Other Configuration Change.", "Connection Collision Resolution.", "Out of Resources.") 666 | 667 | $BGP_NOTIFICATION_CODE_OFFSET = $BGP_HEADER_LEN + 0 668 | $BGP_NOTIFICATION_SUBCODE_OFFSET = $BGP_HEADER_LEN + 1 669 | $BGP_NOTIFICATION_DATA_OFFSET = $BGP_HEADER_LEN + 2 670 | 671 | $BGP_UPDATE_WITHDRAWN_OFFSET = $BGP_HEADER_LEN 672 | $BGP_PATH_ATTRIBUTE_TYPES = @("", "ORIGIN", "AS_PATH", "NEXT_HOP", "MULTI_EXIT_DISC", "LOCAL_PREF", "ATOMIC_AGGREGATE", "AGGREGATOR") 673 | $BGP_PATH_ATTRIBUTE_ORIGIN_VALUE = @("IGP", "EGP", "INCOMPLETE") 674 | $BGP_PATH_ATTRIBUTE_AS_PATH_SEGMENT_TYPE = @("", "AS_SET", "AS_SEQUENCE") 675 | 676 | [BGPState] $Script:bgpState = [BGPState]::Idle 677 | Set-BGPState Idle 678 | $Results = [BGPPeer]::new() 679 | $results.LocalIPAddress = $LocalIPAddress 680 | $results.LocalAS = $LocalASN 681 | $results.RouterIPAddress = $RouterIPAddress 682 | 683 | try { 684 | Write-Verbose "Attempting BGP connection from $localIPAddress to $RouterIPAddress" 685 | Invoke-Command @Session -argumentlist $LocalIPAddress,$RouterIPAddress,$wait,$force { 686 | param( 687 | $LocalIPAddress, 688 | $RouterIPAddress, 689 | $wait, 690 | $force 691 | ) 692 | 693 | $port = "179" 694 | 695 | $RestoreMuxState = $false 696 | $mux = Get-Service -Name 'SlbMux' -ErrorAction SilentlyContinue 697 | if ($null -ne $mux) { 698 | $muxstartup = $mux.starttype 699 | $muxstatus = $mux.status 700 | 701 | if (($muxstatus -ne "Stopped") -or ($Muxstartup -ne "Disabled")) { 702 | if ($force) { 703 | $RestoreMuxState = $true 704 | Set-Service -Name -startup Disabled 705 | stop-Service -Name 706 | } 707 | else { 708 | throw "SLB Mux service is active. Use -force to temporarily disable it during test." 709 | } 710 | } 711 | } 712 | else { 713 | $muxstate = $null 714 | } 715 | 716 | $IPEndpoint = New-object System.Net.IPEndPoint([IPAddress]$LocalIPAddress, 0) 717 | try { 718 | $tcp = New-Object System.Net.Sockets.TcpClient($IPEndpoint) 719 | } 720 | catch { 721 | throw "Local IP address $LocalIPAddress not found on computer $(hostname)." 722 | } 723 | 724 | try { 725 | $tcp.Connect($routerIPAddress, $Port) 726 | } 727 | catch { 728 | throw "BGP Listener not found at RouterIPAddress $RouterIPAddress." 729 | } 730 | 731 | $tcpstream = $tcp.GetStream() 732 | $reader = New-Object System.IO.BinaryReader($tcpStream) 733 | $writer = New-Object System.IO.BinaryWriter($tcpStream) 734 | 735 | $reader.BaseStream.ReadTimeout = $Wait * 1000 736 | } 737 | 738 | Set-BGPState -State Connect 739 | $IsConnected = Invoke-Command @Session { $tcp.connected } 740 | if ($IsConnected) { 741 | Write-Verbose "BGP Connection Initiated." 742 | 743 | #Send OPEN 744 | $chars = new-BGPOpen 745 | 746 | Write-Verbose "Sending BGP OPEN" 747 | Write-Verbose "Write bytes[$($chars.count)] $chars" 748 | 749 | Invoke-Command @Session -argumentlist (, $chars) { 750 | param( 751 | [byte[]] $chars 752 | ) 753 | $writer.Write([byte[]]$chars) 754 | $writer.Flush() 755 | } 756 | 757 | Write-Verbose "Write complete." 758 | Set-BGPState OpenSent 759 | 760 | Write-Verbose "Entering read loop." 761 | do { 762 | try { 763 | $chars = Invoke-Command @Session { 764 | try { 765 | $chars = @() 766 | $chars = @($reader.Readbyte()) 767 | while (($reader.PeekChar() -ne -1) -or ($tcp.Available)) { 768 | $chars += $reader.Readbyte() 769 | } 770 | return $chars 771 | } 772 | catch { 773 | #return @() 774 | if ($_.Exception.InnerException.InnerException.NativeErrorCode -eq 10060) { 775 | #timedout 776 | throw "Timeout" 777 | } 778 | else { 779 | throw $_ 780 | } 781 | } 782 | } 783 | } 784 | catch { 785 | Write-Verbose "Caught. $($_)" 786 | if ($_.exception.Message -eq "Timeout") { 787 | #timedout NativeErrorCode 10060 788 | if (!$bytesRemain) { 789 | Write-Verbose "Timeout, no updates recieved within $Wait seconds. Exiting." 790 | break 791 | } 792 | } 793 | else { 794 | $err = "Connection closed. BGP active at routerIPAddress, but session rejected by remote based on localIPAddress." 795 | Write-Verbose $err 796 | Set-BGPState Idle 797 | throw $err 798 | } 799 | } 800 | $bytesRemain = $chars.count 801 | 802 | while ($bytesremain -gt 0) { 803 | Write-Verbose "Received data, parsing header. Buffer contains $bytesRemain bytes." 804 | Write-Verbose "Buffer bytes[$($chars.count)] $chars" 805 | 806 | $header = Get-BGPHeader $chars 807 | Write-Verbose ($header | ConvertTo-Json -Depth 10) 808 | $bytesRemain -= $header.Length 809 | Write-Verbose "$bytesRemain bytes remain to parse." 810 | 811 | switch ($header.Type) { 812 | "OPEN" { 813 | Write-Verbose "Parsing OPEN message." 814 | $open = Get-BGPOpen $chars 815 | Write-Verbose ($open | ConvertTo-Json -Depth 10) 816 | 817 | $Results.RouterAS = $open.AS 818 | $Results.HoldTime = $open.HoldTime 819 | $Results.Version = $open.Version 820 | $Results.BGPID = $open.BGPID 821 | foreach ($optparam in $open.optparams) { 822 | $op = [BGPOptParam]::New() 823 | $op.Type = $optparam.type 824 | foreach ($cap in $optparam.values) { 825 | $c = [BGPCapability]::new() 826 | $c.Code = $cap.Code 827 | $c.Value = $cap.value 828 | $op.Capabilities += $c 829 | } 830 | $results.OptParams += $op 831 | } 832 | Set-BGPState OpenConfirm 833 | } 834 | "KEEPALIVE" { 835 | if ((Get-BGPState) -in [BGPState]::OpenConfirm, [BGPState]::Established) { 836 | $chars = New-BGPKeepalive 837 | 838 | Write-Verbose "Sending BGP Keepalive" 839 | Write-Verbose "Write bytes[$($chars.count)] $chars" 840 | Invoke-Command @Session -argumentlist (, $chars) { 841 | param( 842 | $chars 843 | ) 844 | $writer.Write([byte[]]$chars) 845 | $writer.Flush() 846 | } 847 | 848 | Set-BGPState -State Established 849 | Write-Verbose "Success, BGP session established!" 850 | } 851 | else { 852 | Write-Verbose "Out of order Keepalive received in state $(Get-BGPState)." 853 | } 854 | } 855 | "NOTIFICATION" { 856 | Write-Verbose "Parsing NOTIFICATION message." 857 | $open = Get-BGPNotification $chars 858 | Write-Verbose ($open | ConvertTo-Json -Depth 10) 859 | Write-Verbose "BGP peer found, but connection refused." 860 | Set-BGPState -State Idle 861 | throw "BGP peer found, but connection refused. ErrorCode: $($open.Errorcode), ErrorSubcode: $($open.ErrorSubcode)" 862 | } 863 | "UPDATE" { 864 | Write-Verbose "Parsing UPDATE message." 865 | $update = Get-bgpupdate $chars 866 | Write-Verbose ($update | ConvertTo-Json -Depth 10) 867 | $NextHop = ($Update.PathAttributes | where-object { $_.AttrType -eq "NEXT_HOP" }).NextHop 868 | $ASPath = ($Update.PathAttributes | where-object { $_.AttrType -eq "AS_PATH" }).ASPath.ASes 869 | $Origin = ($Update.PathAttributes | where-object { $_.AttrType -eq "ORIGIN" }).Value 870 | $LocPrf = ($Update.PathAttributes | where-object { $_.AttrType -eq "LOCAL_PREF" }).Value 871 | $Metric = ($Update.PathAttributes | where-object { $_.AttrType -eq "MULTI_EXIT_DISC" }).Value 872 | 873 | foreach ($prefix in $Update.Prefixes) { 874 | $BGPRoute = [BGPPath]::New() 875 | $BGPRoute.Prefix = $Prefix 876 | $BGPRoute.NextHop = $NextHop 877 | $BGPRoute.Path = $ASPath 878 | $BGPRoute.LocPrf = $LocPrf 879 | $BGPRoute.Metric = $Metric 880 | $BGPRoute.Origin = $Origin 881 | 882 | $Results.Routes += $BGPRoute 883 | } 884 | } 885 | "ROUTEREFRESH" { 886 | Write-Verbose "Parsing ROUTEREFRESH message." 887 | Set-BGPState Custom 888 | } 889 | } 890 | 891 | $chars = getBytes $chars ($header.length) $bytesremain 892 | Write-Verbose "BGP State: $(Get-BGPState)" 893 | Write-Verbose "Returning to read loop, waiting up to $wait seconds for more data." 894 | } 895 | } until ((Get-BGPState) -in [BGPState]::Custom, [BGPState]::Idle) 896 | } 897 | else { 898 | Write-Verbose "Not connected." 899 | throw "Listener found at BGP port 179 of $RouterIPAddress, but it closed the connection from $LocalIPAddress." 900 | } 901 | } 902 | finally { 903 | Invoke-Command @Session { 904 | if ($null -ne $reader) { 905 | $reader.Close() 906 | } 907 | if ($null -ne $writer) { 908 | $writer.Close() 909 | } 910 | if ($null -ne $tcp) { 911 | $tcp.Close() 912 | } 913 | if ($RestoreMuxState) { 914 | Set-Service -Name SlbMux -StartupType $MuxStartup 915 | if ($MuxStatus -eq "Running") { 916 | Start-Service -Name SlbMux 917 | } 918 | } 919 | } 920 | if (![String]::IsNullorEmpty($Computername) ) { 921 | remove-pssession $session.session 922 | } 923 | } 924 | $results.State = (Get-BGPState) 925 | $results 926 | } 927 | 928 | Export-ModuleMember -Function Test-SdnExpressBGP 929 | Export-ModuleMember -Function New-SdnExpressBGPHost 930 | Export-ModuleMember -Function Remove-SdnExpressBGPHost 931 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # The SdnDiagnostics Module test 2 | 3 | The `tests` folder include all the test script use [Pester](https://github.com/pester/Pester). 4 | 5 | ## Offline and Online Tests 6 | 7 | The tests are categorized into two type of tests **offline** and **online** 8 | 9 | - **offline** test can be run without real SDN deployment through mock based on sample data collected from SDN deployment. 10 | - **online** test need to run against SDN deployment 11 | 12 | ## Folder Structure 13 | - `offline\RunTests.ps1` is the start script to run all offline tests under offline test folder. 14 | - `online\RunTests.ps1` is the start script to run all online tests under online folder. 15 | - `wave1`... `waveAll` include all test scripts grouped into different wave. Tests will be executed in order of wave. 16 | 17 | ## Run offline tests 18 | - Install latest Pester by `Install-Module -Name Pester -Force -SkipPublisherCheck`. More info from [Pester Update](https://pester-docs.netlify.app/docs/introduction/installation) 19 | - The `offline\data` folder include the sample data like `SdnApiResources`. The data is loaded into `$Global:PesterOfflineTest` 20 | - Run offline test at offline folder by `.\RunTests.ps1` 21 | 22 | ## Run online tests in your test environment 23 | 24 | - Generate the configuration based on `SdnDiagnosticsTestConfig-Sample.psd1`. Do not commit change to include your test environment specific settings. 25 | - Copy the `tests` folder to the test environment and run 26 | 27 | `.\RunTests.ps1 -ConfigurationFile SdnDiagnosticsTestConfig-Sample.psd1` 28 | 29 | ## To create new tests 30 | 31 | - If your test function can be mocked with sample data, put it under `offline` folder. Otherwise, this have to be under `online` folder. 32 | - For offline test, sample data can be consumed from `$Global:PesterOfflineTest` to write your mock. 33 | - The new test script should be named as `*originalscriptname*.Tests.ps1`. For example, `Diagnostics.Tests.ps1` include the tests function for script `Diagnostics.ps1` 34 | - The online test scripts are grouped into different wave to maintain test execution order. `wave1` ... `waveAll` . If you don't expect order of test execution, the test script need to be in `waveAll` folder. 35 | -------------------------------------------------------------------------------- /tests/offline/NetworkController.Tests.ps1: -------------------------------------------------------------------------------- 1 | Describe 'NetworkController test' { 2 | BeforeAll { 3 | Mock -ModuleName SdnDiagnostics Get-SdnResource { 4 | return $Global:PesterOfflineTests.SdnApiResources[$ResourceType.ToString()] 5 | } 6 | } 7 | It "Get-SdnServer -ManagementAddressOnly should return Server Address Only" { 8 | $servers = Get-SdnServer "https://sdnexpnc" -ManagementAddressOnly 9 | $servers.Count | Should -BeGreaterThan 0 10 | $servers[0].GetType() | Should -Be "String" 11 | } 12 | 13 | It "Get-SdnServer should return Server resource" { 14 | $servers = Get-SdnServer "https://sdnexpnc" 15 | $servers.Count | Should -BeGreaterThan 0 16 | $servers[0].resourceRef | Should -Not -BeNullOrEmpty 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/offline/RunTests.ps1: -------------------------------------------------------------------------------- 1 | # Load the baseline test data needed for Pester Mock 2 | 3 | $modulePath = Get-Item -Path "$PSScriptRoot\..\..\out\build\SdnDiagnostics\SdnDiagnostics.psd1" -ErrorAction SilentlyContinue 4 | if($null -eq $modulePath){ 5 | "Unable to locate module. Generate a local build first" | Write-Host -ForegroundColor:Yellow 6 | return 7 | } 8 | 9 | # API resources 10 | $sdnApiResourcesPath = "$PSScriptRoot\data\SdnApiResources" 11 | $Global:PesterOfflineTests = @{} 12 | $Global:PesterOfflineTests.SdnApiResources = @{} 13 | foreach($file in Get-ChildItem -Path $sdnApiResourcesPath) 14 | { 15 | $Global:PesterOfflineTests.SdnApiResources[$file.BaseName] = Get-Content -Path $file.FullName | ConvertFrom-Json 16 | } 17 | 18 | $Global:PesterOfflineTests.SdnApiResourcesByRef = [System.Collections.Hashtable]::new() 19 | foreach($resourceType in $Global:PesterOfflineTests.SdnApiResources.Keys) 20 | { 21 | $resourcesOfType = $Global:PesterOfflineTests.SdnApiResources[$resourceType] 22 | foreach($resource in $resourcesOfType) 23 | { 24 | if($null -ne $resource.resourceRef){ 25 | $Global:PesterOfflineTests.SdnApiResourcesByRef.Add($resource.resourceRef, $resource) 26 | } 27 | } 28 | } 29 | 30 | Import-Module -Name $modulePath.FullName -Force 31 | 32 | Invoke-Pester "$PSScriptRoot\*Tests.ps1" -Output Detailed -------------------------------------------------------------------------------- /tests/offline/SoftwareLoadBalancer.Tests.ps1: -------------------------------------------------------------------------------- 1 | Describe 'LoadBalancerMux test' { 2 | BeforeAll { 3 | Mock -ModuleName SdnDiagnostics Get-SdnResource { 4 | if(![string]::IsNullOrEmpty($ResourceRef)){ 5 | return $Global:PesterOfflineTests.SdnApiResourcesByRef[$ResourceRef] 6 | } 7 | else { 8 | return $Global:PesterOfflineTests.SdnApiResources[$ResourceType.ToString()] 9 | } 10 | } 11 | } 12 | It "Get-SdnNetworkInterfaceOutboundPublicIPAddress able to return Public VIP from Outbound NAT Rule" { 13 | $publicIpInfo = Get-SdnNetworkInterfaceOutboundPublicIPAddress -NcUri "https://sdnexpnc" -ResourceId tenantvm2 14 | $publicIpInfo.PublicIPAddress | Should -Be "40.40.40.4" 15 | $publicIpInfo.IPConfigPrivateIPAddress | Should -Be "192.168.33.5" 16 | } 17 | 18 | It "Get-SdnNetworkInterfaceOutboundPublicIPAddress able to return Public VIP on network interface" { 19 | $publicIpInfo = Get-SdnNetworkInterfaceOutboundPublicIPAddress -NcUri "https://sdnexpnc" -ResourceId tenantvm1 20 | $publicIpInfo.PublicIPAddress | Should -Be "40.40.40.5" 21 | $publicIpInfo.IPConfigPrivateIPAddress | Should -Be "192.168.33.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/offline/data/Get-SdnInfrastructureInfo.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/Get-SdnInfrastructureInfo.xml -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/accessControlLists.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/accessControlLists.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/credentials.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/credentials.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/gatewayPools.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/gatewayPools.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/gateways.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/gateways.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/iDNSServer_configuration.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/iDNSServer_configuration.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/loadBalancerManager_config.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/loadBalancerManager_config.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/loadBalancerMuxes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/loadBalancerMuxes.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/loadBalancers.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/loadBalancers.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/logicalNetworks.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/logicalNetworks.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/macPools.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/macPools.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/networkInterfaces.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/networkInterfaces.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/publicIPAddresses.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/publicIPAddresses.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/routeTables.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/routeTables.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/servers.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/servers.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/virtualGateways.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/virtualGateways.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/virtualNetworkManager_configuration.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/virtualNetworkManager_configuration.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/virtualNetworks.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/virtualNetworks.json -------------------------------------------------------------------------------- /tests/offline/data/SdnApiResources/virtualServers.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/SdnDiagnostics/8d39b5bc616c82071b0cee7a5c67b342abcefd30/tests/offline/data/SdnApiResources/virtualServers.json -------------------------------------------------------------------------------- /tests/online/RunTests.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Parameter(Mandatory = $true)] 3 | [String] $ConfigurationFile 4 | ) 5 | $Global:PesterOnlineTests = @{ 6 | } 7 | 8 | $Global:PesterOnlineTests.ConfigData = [hashtable] (Invoke-Expression (Get-Content -Path $ConfigurationFile | Out-String)) 9 | 10 | $Global:PesterOnlineTests.NcRestCredential = [System.Management.Automation.PSCredential]::Empty 11 | #$ncAdminCredential = [System.Management.Automation.PSCredential]::Empty 12 | if($null -ne $Global:PesterOnlineTests.ConfigData.NcRestCredentialUser){ 13 | $ncRestSecurePassword = $Global:PesterOnlineTests.ConfigData.NcRestCredentialPassword | ConvertTo-SecureString 14 | $Global:PesterOnlineTests.NcRestCredential = New-Object System.Management.Automation.PsCredential($Global:PesterOnlineTests.ConfigData.NcRestCredentialUser, $ncRestSecurePassword) 15 | } 16 | 17 | if($null -eq $Global:PesterOnlineTests.ConfigData.SdnDiagnosticsModule) 18 | { 19 | $modulePathFromBuild = "$PSScriptRoot\..\..\out\build\SdnDiagnostics\SdnDiagnostics.psd1" 20 | "Importing module from {0}" -f $modulePathFromBuild | Write-Output 21 | Import-Module $modulePathFromBuild 22 | }else { 23 | Import-Module $Global:PesterOnlineTests.ConfigData.SdnDiagnosticsModule -Force 24 | } 25 | 26 | # Tests can be arranged in different wave if order matters 27 | $testFailed = 0 28 | $testResult = Invoke-Pester "$PSScriptRoot\wave1\*Tests.ps1" -Output Detailed -PassThru 29 | if($testResult.Result -ne "Passed") 30 | { 31 | $testFailed = 1 32 | } 33 | $testResult = Invoke-Pester "$PSScriptRoot\waveAll\*Tests.ps1" -Output Detailed -PassThru 34 | if($testResult.Result -ne "Passed") 35 | { 36 | $testFailed = 1 37 | } 38 | 39 | # Exit code 0 indicate success 40 | return $testFailed -------------------------------------------------------------------------------- /tests/online/SdnDiagnosticsTestConfig-Sample.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Required. Specify the one of NC VM Name for tests to start with 3 | NcVM = 'sdnexpnc01.corp.contoso.com' 4 | 5 | # Configure NcRestCredential if needed 6 | # NcRestCredentialUser = 'domain\user' 7 | 8 | # The Password need to be secure string from (Get-Credential).Password | ConvertFrom-SecureString 9 | # NcRestCredentialPassword = 'YourPassword' 10 | 11 | # Required. Specify the SdnDiagnosticsModule Path 12 | # SdnDiagnosticsModule = '' 13 | 14 | # The number of each infra node. This will ensure the module able to get information match the test environment. 15 | NumberOfNc = 3 16 | NumberOfMux = 2 17 | NumberOfServer = 3 18 | NumberOfGateway = 2 19 | } -------------------------------------------------------------------------------- /tests/online/wave1/Utilities.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Pester tests 2 | Describe 'Install-SdnDiagnostics test' { 3 | It "Install-SdnDiagnostics installed SdnDiagnostic Module successfully" { 4 | $infraInfo = Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential 5 | Install-SdnDiagnostics -ComputerName $infraInfo.fabricNodes 6 | 7 | $currentModule = Get-Module SdnDiagnostics 8 | 9 | $remoteModuleInfo = Invoke-Command -ComputerName $infraInfo.fabricNodes -ScriptBlock{ 10 | return (Get-Module -ListAvailable -Name SdnDiagnostics) 11 | } 12 | 13 | foreach ($moduleInfo in $remoteModuleInfo) { 14 | $moduleInfo.Version | Should -Be $currentModule.Version 15 | } 16 | #$infraInfo.NCUrl | Should -not -BeNullOrEmpty 17 | } 18 | } -------------------------------------------------------------------------------- /tests/online/waveAll/Debug-SdnFabricInfrastructure.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Pester tests 2 | Describe 'Debug-SdnFabricInfrastructure test' { 3 | It "Debug-SdnFabricInfrastrucure run all debug with no exception" { 4 | $result = Debug-SdnFabricInfrastructure -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential 5 | $result | Should -not -BeNullOrEmpty 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/online/waveAll/Get-SdnInfrastructureInfo.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Pester tests 2 | Describe 'Get-SdnInfrastructureInfo test' { 3 | It "Able to retreive NCUrl" { 4 | $infraInfo = Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential 5 | $infraInfo.NCUrl | Should -not -BeNullOrEmpty 6 | } 7 | It "All NC retrieved" { 8 | $infraInfo = Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential 9 | $infraInfo.NetworkController.Count | Should -Be $Global:PesterOnlineTests.ConfigData.NumberOfNc 10 | } 11 | It "All MUX retrieved" { 12 | $infraInfo = Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential 13 | $infraInfo.LoadBalancerMux.Count | Should -Be $Global:PesterOnlineTests.ConfigData.NumberOfMux 14 | } 15 | It "All Gateway retrieved" { 16 | $infraInfo = Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential 17 | $infraInfo.Gateway.Count | Should -Be $Global:PesterOnlineTests.ConfigData.NumberOfGateway 18 | } 19 | 20 | It "All Server retrieved" { 21 | $infraInfo = Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential 22 | $infraInfo.Server.Count | Should -Be $Global:PesterOnlineTests.ConfigData.NumberOfServer 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/online/waveAll/Start-SdnDataCollection.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Pester tests 2 | Describe 'Start-SdnDataCollection test' { 3 | It "Start-SdnNetshTrace successfully started trace on Server" { 4 | { Start-SdnNetshTrace -ComputerName (Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential).Server -Role Server} | Should -Not -Throw 5 | } 6 | 7 | It "Start-SdnNetshTrace successfully started trace on Mux" { 8 | { Start-SdnNetshTrace -ComputerName (Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential).LoadBalancerMux -Role LoadBalancerMux} | Should -Not -Throw 9 | } 10 | 11 | It "Start-SdnNetshTrace successfully started trace on Gateway" { 12 | { Start-SdnNetshTrace -ComputerName (Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential).Gateway -Role Gateway} | Should -Not -Throw 13 | } 14 | 15 | Start-Sleep -Seconds 60 16 | 17 | It "Stop-SdnNetshTrace successfully stop trace on Server" { 18 | { Stop-SdnNetshTrace -ComputerName (Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential).Server} | Should -Not -Throw 19 | } 20 | 21 | It "Stop-SdnNetshTrace successfully stop trace on LoadBalancerMux" { 22 | { Stop-SdnNetshTrace -ComputerName (Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential).LoadBalancerMux} | Should -Not -Throw 23 | } 24 | 25 | It "Stop-SdnNetshTrace successfully stop trace on Gateway" { 26 | { Stop-SdnNetshTrace -ComputerName (Get-SdnInfrastructureInfo -NetworkController $Global:PesterOnlineTests.configdata.NcVM -NcRestCredential $Global:PesterOnlineTests.NcRestCredential).Gateway} | Should -Not -Throw 27 | } 28 | 29 | It "Start-SdnDataCollection successfully collected the logs" { 30 | { Start-SdnDataCollection -NetworkController $Global:PesterOnlineTests.configdata.NcVM -Role NetworkController,LoadBalancerMux,Gateway,Server -OutputDirectory "$PSScriptRoot\..\..\DataCollected" -IncludeLogs -NcRestCredential $Global:PesterOnlineTests.NcRestCredential } | Should -Not -Throw 31 | } 32 | } 33 | --------------------------------------------------------------------------------