├── .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 | [](https://scorecard.dev/viewer/?uri=github.com/microsoft/SdnDiagnostics)  
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 | [](https://www.powershellgallery.com/packages/SdnDiagnostics) | [](https://www.powershellgallery.com/packages/SdnDiagnostics) |
12 | | NuGet | [](https://www.nuget.org/packages/SdnDiagnostics) |[](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 |
--------------------------------------------------------------------------------