├── .build.ps1 ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── enhancement-request.md └── pull_request_template.md ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── config ├── 1-tier-app.json ├── 2-tier-app.json └── 3-tier-app.json ├── docs └── quick-start.md ├── environment ├── se-1.json └── se-2.json ├── helper ├── CodeSamples.ps1 ├── Show-BuildGraph.ps1 ├── demoParallel.ps1 ├── demoSerial.ps1 └── generateCreds.ps1 ├── img ├── build-graph.png ├── image1.png ├── image10.png ├── image11.png ├── image12.png ├── image13.png ├── image14.png ├── image15.png ├── image16.png ├── image17.png ├── image18.png ├── image19.png ├── image2.png ├── image3.png ├── image4.png ├── image5.png ├── image6.png ├── image7.png ├── image8.png └── image9.png └── tests.ps1 /.build.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | # Path to the environment JSON file used to identify the vCenter and Rubrik servers 3 | [Parameter(Mandatory=$true)] 4 | [ValidateScript({Test-Path $_})] 5 | [String]$EnvironmentFile, 6 | # Path to the configuration JSON file used to describe the applications being tested 7 | [Parameter(Mandatory=$true)] 8 | [ValidateScript({Test-Path $_})] 9 | [String]$ConfigFile, 10 | # Path to the folder that contains XML credential files for this build 11 | [Parameter(Mandatory=$true)] 12 | [ValidateScript({Test-Path $_})] 13 | [String]$IdentityPath 14 | ) 15 | 16 | # Synopsis: Pull configuration details from the root config.json file 17 | task GetConfig { 18 | $script:Environment = Get-Content -Path $EnvironmentFile | ConvertFrom-Json 19 | $script:Config = Get-Content -Path $ConfigFile | ConvertFrom-Json 20 | # If a trailing backslash is omitted, this will make sure it's added to correct for future path + filename activities 21 | if ($IdentityPath.Substring($IdentityPath.Length - 1) -ne '\') { 22 | $script:IdentityPath += '\' 23 | } 24 | } 25 | 26 | # Synopsis: Establish connectivity to a Rubrik Cluster 27 | task ConnectRubrik { 28 | Write-Verbose -Message "Importing Credential file: $($IdentityPath + $Environment.rubrikCred)" 29 | $Credential = Import-Clixml -Path ($IdentityPath + $Environment.rubrikCred) 30 | $null = Connect-Rubrik -Server $Environment.rubrikServer -Credential $Credential 31 | Write-Verbose -Message "Rubrik Status: Connected to $($rubrikConnection.server)" -Verbose 32 | } 33 | 34 | # Synopsis: Establish connectivity to a VMware vCenter Server 35 | task ConnectVMware { 36 | Write-Verbose -Message "Importing Credential file: $($IdentityPath + $Environment.vmwareCred)" 37 | $Credential = Import-Clixml -Path ($IdentityPath + $Environment.vmwareCred) 38 | $null = Connect-VIServer -Server $Environment.vmwareServer -Credential $Credential 39 | Write-Verbose -Message "VMware Status: Connected to $($global:DefaultVIServer.Name)" -Verbose 40 | } 41 | 42 | # Synopsis: Create a Live Mount of the intended virtual machine(s) 43 | task CreateLiveMount { 44 | $i = 0 45 | # Uses a null array of Mount IDs that will be used to track the request process 46 | [Array]$Script:MountArray = $null 47 | foreach ($VM in $Config.virtualMachines) { 48 | # Check if there is already an existing live mount with the same name 49 | if ( ( $MountTest = (Get-RubrikMount).mountedVmId | Where-Object {$_.total -ne 0} ) ) { 50 | if ($MountTest | 51 | ForEach-Object { 52 | (Get-RubrikVM -ID $_ -EA 0).name 53 | } | Select-String -Pattern "^$($VM.mountName)$" ) { 54 | throw "The live mount $($VM.mountName) already exists. Please remove manually." 55 | } 56 | } 57 | # The resulting Live Mount has the network interface disabled 58 | $MountRequest = Get-RubrikVM $VM.name | 59 | Get-RubrikSnapshot -Date (Get-Date) | Where-Object {$_.id} | ForEach-Object { 60 | New-RubrikMount -Id $_.id -MountName $VM.mountName -PowerOn:$true -DisableNetwork:$true -Confirm:$false 61 | } 62 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) Request Created: $($MountRequest.id)" -Verbose 63 | $Script:MountArray += $MountRequest 64 | $i++ 65 | } 66 | } 67 | 68 | # Synopsis: Validate the health of the Live Mount request and power state 69 | task ValidateLiveMount { 70 | $i = 0 71 | foreach ($Mount in $MountArray) { 72 | while ($true) { 73 | $ValidateRequest = (Get-RubrikRequest -id $Mount.id -Type vmware/vm).status 74 | $ValidatePowerOn = (Get-VM -Name $Config.virtualMachines[$i].mountName -ErrorAction:SilentlyContinue).PowerState 75 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) Status: Request is $ValidateRequest, PowerState is $ValidatePowerOn" -Verbose 76 | if ($ValidateRequest -in @('RUNNING','ACQUIRING','QUEUED','FINISHING')) { 77 | Start-Sleep 5 78 | } elseif ($ValidateRequest -eq 'SUCCEEDED' -and $ValidatePowerOn -eq 'PoweredOn') { 79 | break 80 | } else { 81 | throw "$($Config.virtualMachines[$i].mountName) mount failed, exiting Build script. Previously live mounted VMs will continue running" 82 | } 83 | } 84 | $i++ 85 | } 86 | } 87 | 88 | # Synopsis: Validate the health of the Live Mount VMware Tools 89 | task ValidateLiveMountTools { 90 | $i = 0 91 | foreach ($Mount in $MountArray) { 92 | while ($true) { 93 | $ValidateTools = (Get-VM -Name $Config.virtualMachines[$i].mountName).ExtensionData.Guest.ToolsRunningStatus 94 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) VMware Tools Status: $ValidateTools" -Verbose 95 | if ($ValidateTools -ne 'guestToolsRunning') { 96 | Start-Sleep 5 97 | } else { 98 | break 99 | } 100 | } 101 | $i++ 102 | } 103 | } 104 | 105 | task ValidateRemoteScriptExecution { 106 | $i = 0 107 | foreach ($Mount in $MountArray) { 108 | $LoopCount = 1 109 | # Keeping the guest credential value local since it may only apply to the individual virtual machine in some cases 110 | # Try per vm guest credentials first 111 | if ( Get-Variable -Name "$Config.virtualMachines[$i].guestCred" -ErrorAction SilentlyContinue ) { 112 | Write-Verbose -Message "Importing Credential file: $($IdentityPath + $($Config.virtualMachines[$i].guestCred))" -Verbose 113 | $GuestCredential = Import-Clixml -Path ($IdentityPath + $($Config.virtualMachines[$i].guestCred)) 114 | } 115 | # Use global guest credentials 116 | else { 117 | Write-Verbose -Message "Importing Credential file: $($IdentityPath + "guestCred.XML")" -Verbose 118 | $GuestCredential = Import-Clixml -Path ($IdentityPath + "guestCred.XML") 119 | } 120 | while ($true) { 121 | Write-Verbose -Message "Testing script execution on '$($Config.virtualMachines[$i].mountName)', attempt '$LoopCount'..." -Verbose 122 | $splat = @{ 123 | ScriptText = 'hostname' 124 | ScriptType = 'PowerShell' 125 | VM = $Config.virtualMachines[$i].mountName 126 | GuestCredential = $GuestCredential 127 | } 128 | try { 129 | $VMScript_return = Invoke-VMScript @splat -ErrorAction Stop 130 | Write-Verbose -Message "ValidateRemoteScriptExecution returned '$VMScript_return'" 131 | break 132 | } catch { } 133 | 134 | $LoopCount++ 135 | Sleep -Seconds 5 136 | 137 | if ($LoopCount -gt 5) { 138 | throw "Could not execute script on: $($Config.virtualMachines[$i].mountName)..." 139 | } 140 | } 141 | $i++ 142 | } 143 | } 144 | 145 | # Synopsis: Move a Live Mount to a test network 146 | task MoveLiveMountNetwork { 147 | $i = 0 148 | foreach ($Mount in $MountArray) { 149 | $SplatNetAdapter = @{ 150 | NetworkName = $Config.virtualMachines[$i].testNetwork 151 | Connected = $true 152 | Confirm = $false 153 | } 154 | $ValidateNetwork = Get-NetworkAdapter -VM $Config.virtualMachines[$i].mountName | 155 | Set-NetworkAdapter @SplatNetAdapter 156 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) Network Status: $($ValidateNetwork.NetworkName) is $($ValidateNetwork.ConnectionState)" -Verbose 157 | $i++ 158 | } 159 | } 160 | 161 | # Synopsis: Move a Live Mount to a test address 162 | task MoveLiveMountNetworkAddress { 163 | $i = 0 164 | foreach ($Mount in $MountArray) { 165 | # Keeping the guest credential value local since it may only apply to the individual virtual machine in some cases 166 | # Try per vm guest credentials first 167 | if ( Get-Variable -Name "$Config.virtualMachines[$i].guestCred" -ErrorAction SilentlyContinue ) { 168 | Write-Verbose -Message "Importing Credential file: $($IdentityPath + $($Config.virtualMachines[$i].guestCred))" -Verbose 169 | $GuestCredential = Import-Clixml -Path ($IdentityPath + $($Config.virtualMachines[$i].guestCred)) 170 | } 171 | # Use global guest credentials 172 | else { 173 | Write-Verbose -Message "Importing Credential file: $($IdentityPath + "guestCred.XML")" -Verbose 174 | $GuestCredential = Import-Clixml -Path ($IdentityPath + "guestCred.XML") 175 | } 176 | # Find the first network interface's MAC 177 | $TestInterfaceMAC = ((Get-NetworkAdapter -VM $Config.virtualMachines[$i].mountName | Select-Object -first 1).MacAddress).ToLower() -replace ":","-" 178 | $splat = @{ 179 | ScriptText = 'Get-NetAdapter | where {($_.MacAddress).ToLower() -eq "' + $TestInterfaceMAC + '"} | Remove-NetRoute -Confirm:$false -ErrorAction SilentlyContinue;` 180 | Get-NetAdapter | where {($_.MacAddress).ToLower() -eq "' + $TestInterfaceMAC + '"} | Get-NetIPAddress | Remove-NetIPAddress -confirm:$false;` 181 | Get-NetAdapter | where {($_.MacAddress).ToLower() -eq "' + $TestInterfaceMAC + '"} | Set-NetIPInterface -DHCP Disable;` 182 | Get-NetAdapter | where {($_.MacAddress).ToLower() -eq "' + $TestInterfaceMAC + '"} | ` 183 | New-NetIPAddress -IPAddress ' + $Config.virtualMachines[$i].testIp + ' -PrefixLength ' + $Config.virtualMachines[$i].testSubnet + ` 184 | ' -DefaultGateway ' + $Config.virtualMachines[$i].testGateway 185 | ScriptType = 'PowerShell' 186 | VM = $Config.virtualMachines[$i].mountName 187 | GuestCredential = $GuestCredential 188 | } 189 | Write-Verbose -Message "Changing ip of $($Config.virtualMachines[$i].mountName) to $($Config.virtualMachines[$i].testIp)." -Verbose 190 | $output = Invoke-VMScript @splat -ErrorAction Stop 191 | $splat = @{ 192 | ScriptText = 'function Elevate-Process { 193 | param ([string]$exe = $(Throw "Pleave provide the name and path of an executable"),[string]$arguments) 194 | $startinfo = new-object System.Diagnostics.ProcessStartInfo 195 | $startinfo.FileName = $exe 196 | $startinfo.Arguments = $arguments 197 | $startinfo.verb = "RunAs" 198 | $process = [System.Diagnostics.Process]::Start($startinfo) 199 | } 200 | function QueryIP { 201 | Get-NetAdapter| where {($_.MacAddress).ToLower() -eq "' + $TestInterfaceMAC + '"} 202 | | Get-NetIPAddress -AddressFamily IPv4).IPAddress 203 | } 204 | Elevate-Process -Exe powershell.exe -Arguments "-noninteractive -command QueryIP > C:\rubrik.txt"' 205 | ScriptType = 'PowerShell' 206 | VM = $Config.virtualMachines[$i].mountName 207 | GuestCredential = $GuestCredential 208 | } 209 | Write-Verbose -Message "Verifying new ip of $($Config.virtualMachines[$i].mountName)." -Verbose 210 | $output = Invoke-VMScript @splat -ErrorAction Stop 211 | $new_ip = $($output.ScriptOutput | Out-String).Trim().Split("`r`n")[-1] 212 | if ( $new_ip -eq $Config.virtualMachines[$i].testIp ) { 213 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) Network Address Status: Assigned to $($new_ip)" 214 | } 215 | else { 216 | throw "$($Config.virtualMachines[$i].mountName) changing ip to $($Config.virtualMachines[$i].testIp) failed, exiting Build script. Previously live mounted VMs will continue running" 217 | } 218 | $i++ 219 | } 220 | } 221 | 222 | # Synopsis: Validate the Live Mount against one or more tests to verify the backup copy is operational 223 | task LiveMountTest { 224 | $i = 0 225 | foreach ($Mount in $MountArray) { 226 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) Test Status: Loading the following tests - $($Config.virtualMachines[$i].tasks)" -Verbose 227 | # Keeping the guest credential value local since it may only apply to the individual virtual machine in some cases 228 | # Not all tests will need a guest credential, but it's there in case required 229 | # Try per vm guest credentials first 230 | if ( Get-Variable -Name "$Config.virtualMachines[$i].guestCred" -ErrorAction SilentlyContinue ) { 231 | Write-Verbose -Message "Importing Credential file: $($IdentityPath + $($Config.virtualMachines[$i].guestCred))" -Verbose 232 | $GuestCredential = Import-Clixml -Path ($IdentityPath + $($Config.virtualMachines[$i].guestCred)) 233 | } 234 | # Use global guest credentials 235 | else { 236 | Write-Verbose -Message "Importing Credential file: $($IdentityPath + "guestCred.XML")" -Verbose 237 | $GuestCredential = Import-Clixml -Path ($IdentityPath + "guestCred.XML") 238 | } 239 | Invoke-Build -File .\tests.ps1 -Task $Config.virtualMachines[$i].tasks -Config $Config.virtualMachines[$i] -GuestCredential $GuestCredential 240 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) Test Status: Testing complete" -Verbose 241 | $i++ 242 | } 243 | } 244 | 245 | # Synopsis: Remove any remaining Live Mount artifacts 246 | task Cleanup { 247 | $i = 0 248 | foreach ($Mount in $MountArray) { 249 | # The request may take a few seconds to complete, but it's not worth holding up the build waiting for the task 250 | $UnmountRequest = Get-RubrikMount -id (Get-RubrikRequest -id $Mount.id -Type vmware/vm).links.href[0].split('/')[-1] | Remove-RubrikMount -Force -Confirm:$false 251 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) Removal Status: $($UnmountRequest.id) is $($UnmountRequest.status)" -Verbose 252 | $i++ 253 | } 254 | } 255 | 256 | task CleanAll { 257 | foreach ($VM in $Config.virtualMachines) { 258 | # Check if there is already an existing live mount with the same name 259 | if ( $MountTest = (Get-RubrikMount -VMID (Get-RubrikVM -VM "$VM.name").id) | Where-Object {$_.total -ne 0} ) { 260 | $MountTest | ForEach-Object { 261 | If ( (Get-RubrikVM -ID $_ -EA 0).name -eq $VM.mountName ) { 262 | Write-Verbose -Message "$($Config.virtualMachines[$i].mountName) removing this live mount" -Verbose 263 | Remove-RubrikMount $_ 264 | } 265 | } 266 | } 267 | } 268 | } 269 | 270 | 271 | task 1_Init ` 272 | GetConfig 273 | 274 | task 2_Connect ` 275 | ConnectRubrik, 276 | ConnectVMware 277 | 278 | task 3_LiveMount ` 279 | CreateLiveMount, 280 | ValidateLiveMount, 281 | ValidateLiveMountTools, 282 | ValidateRemoteScriptExecution 283 | 284 | task 4_LiveMountNetwork ` 285 | MoveLiveMountNetworkAddress, 286 | MoveLiveMountNetwork 287 | 288 | task 5_Testing ` 289 | LiveMountTest 290 | 291 | task . ` 292 | 1_Init, 293 | 2_Connect, 294 | 3_LiveMount, 295 | 4_LiveMountNetwork, 296 | 5_Testing, 297 | Cleanup -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug encountered while using the Rubrik Use Case for Backup Validation via PowerShell. 4 | title: '' 5 | labels: kind-bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | **Expected Behavior** 14 | 15 | Please describe the behavior you are expecting. 16 | 17 | **Current Behavior** 18 | 19 | What is the current behavior? 20 | 21 | **Failure Information (for bugs)** 22 | 23 | Please help provide information about the failure if this is a bug. 24 | 25 | 26 | * Use verbose outputs to capture any debug information. 27 | ``` 28 | Paste into a code block. 29 | ``` 30 | 31 | **Steps to Reproduce** 32 | 33 | Please provide detailed steps for reproducing the issue. 34 | 35 | * Use numbered list. 36 | 37 | **Context** 38 | 39 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions. 40 | 41 | * Version of project. 42 | * Version of dependencies. 43 | * Version of operating system. 44 | 45 | **Failure Logs** 46 | 47 | Please include any relevant log snippets or files here. 48 | 49 | * Use verbose outputs to capture any debug information. 50 | 51 | ``` 52 | Paste into a code block. 53 | ``` 54 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Request 3 | about: Suggest an enhancement to the Rubrik Use Case for Backup Validation via PowerShell 4 | title: '' 5 | labels: kind-enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | 14 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 15 | 16 | **Describe the solution you'd like** 17 | 18 | A clear and concise description of what you want to happen. 19 | 20 | **Describe alternatives you've considered** 21 | 22 | A clear and concise description of any alternative solutions or features you've considered. 23 | 24 | **Additional context** 25 | 26 | Add any other context or screenshots about the feature request here. 27 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please describe your pull request in detail. 4 | 5 | ## Related Issue 6 | 7 | This project only accepts pull requests related to open issues. 8 | 9 | * If suggesting a new feature or change, please discuss it in an issue first. 10 | * If fixing a bug, there should be an issue describing it with steps to reproduce 11 | 12 | _Please link to the issue here_ 13 | 14 | ## Motivation and Context 15 | 16 | Why is this change required? What problem does it solve? 17 | 18 | ## How Has This Been Tested? 19 | 20 | * Please describe in detail how you tested your changes. 21 | * Include details of your testing environment, and the tests you ran to see how your change affects other areas of the code, etc. 22 | 23 | ## Screenshots (if appropriate): 24 | 25 | ## Types of changes 26 | 27 | What types of changes does your code introduce? Put an `x` in all the boxes that apply: 28 | - [ ] Bug fix (non-breaking change which fixes an issue) 29 | - [ ] New feature (non-breaking change which adds functionality) 30 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 31 | 32 | ## Checklist: 33 | 34 | Go over all the following points, and put an `x` in all the boxes that apply. If you're unsure about any of these, don't hesitate to ask. We're here to help! 35 | - [ ] My code follows the code style of this project. 36 | - [ ] My change requires a change to the documentation. 37 | - [ ] I have updated the documentation accordingly. 38 | - [ ] I have read the **[CONTRIBUTION](https://github.com/rubrikinc/use-case-powershell-backup-validation/blob/master/CONTRIBUTING.md)** document. 39 | - [ ] I have updated the CHANGELOG file accordingly for the version that this merge modifies. 40 | - [ ] I have added tests to cover my changes. 41 | - [ ] All new and existing tests passed. 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db 38 | 39 | # Custom # 40 | ###################### 41 | .vscode/ 42 | credential/ 43 | config/TestConf.json 44 | environment/TestEnv.json -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributions via GitHub pull requests are gladly accepted from their original author. Along with any pull requests, please state that the contribution is your original work and that you license the work to the project under the project's open source license. Whether or not you state this explicitly, by submitting any copyrighted material via pull request, email, or other means you agree to license the material under the project's open source license and warrant that you have the legal authority to do so. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Rubrik 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 | # PowerShell Backup Validation 2 | 3 | This project is used to provide a framework for serial and parallel application testing against workloads that have been protected by Rubrik's Cloud Data Management platform. 4 | 5 | Using the `Invoke-Build` framework, this project allows for an administrator to declare the topology of an application across one or more virtual machines. The entire collection of virtual machines are Live Mounted as a group and a battery of user-defined tests are applied. Upon the completion of the tests, the Live Mounts are removed and a summary of results are displayed. 6 | 7 | # :white_check_mark: Prerequisites 8 | 9 | There are a few services you'll need in order to get this project off the ground: 10 | 11 | * [PowerShell](https://aka.ms/getps6) 12 | * [Rubrik PowerShell Module](https://www.powershellgallery.com/packages/Rubrik/) 13 | * [Rubrik PowerShell Backup Validation Module](https://github.com/rubrikinc/rubrik-module-for-powershell-backup-validation) 14 | * [VMware PowerCLI](https://www.powershellgallery.com/packages/VMware.PowerCLI/) 15 | * [InvokeBuild](https://www.powershellgallery.com/packages/InvokeBuild/) 16 | 17 | 18 | # :hammer: Installation 19 | 20 | This folder can be dropped anywhere on your workstation that has network connectivity to a Rubrik cluster and related vCenter Server. 21 | 22 | ## Configuration 23 | 24 | There are three main points of configuration: Environment JSON files, Config JSON files, and Identity XML files. 25 | 26 | ### Environment JSON Files 27 | 28 | The `Environment` folder contains JSON files that describe the Rubrik Cluster and vCenter Server information. A sample configuration looks like: 29 | 30 | ```PowerShell 31 | { 32 | "rubrikServer": "172.17.28.11", 33 | "rubrikCred": "rubrikCred.xml", 34 | "vmwareServer": "172.17.48.22", 35 | "vmwareCred": "vmwareCred.xml" 36 | } 37 | ``` 38 | 39 | ### Config JSON Files 40 | 41 | The `Config` folder contains JSON files that describe the virtual machines being tested. A sample configuration looks like: 42 | 43 | ```PowerShell 44 | { 45 | "virtualMachines": [ 46 | { 47 | "name": "SE-CWAHL-WIN", 48 | "mountName": "MOUNT-2TIER-APP", 49 | "guestCred": "guestCred.xml", 50 | "testIp": "172.17.50.121", 51 | "testNetwork": "VLAN50_Servers_Mount", 52 | "testGateway": "172.17.50.1", 53 | "tasks": ["Ping","Netlogon"] 54 | }, 55 | { 56 | "name": "SE-CWAHL-WIN", 57 | "mountName": "MOUNT-2TIER-DB", 58 | "guestCred": "guestCred.xml", 59 | "testIp": "172.17.50.122", 60 | "testNetwork": "VLAN50_Servers_Mount", 61 | "testGateway": "172.17.50.1", 62 | "tasks": ["Ping","Netlogon"] 63 | } 64 | ] 65 | } 66 | ``` 67 | 68 | ### Identity 69 | 70 | The `Identity` folder is not included in this repository. It can be placed anywhere in your environment and should host secure XML files created with `Export-Clixml` containing the credentials needed to communicate with the Rubrik cluster, vCenter Server, and any guest operating systems involved in the application testing. 71 | 72 | Use the [generateCreds.ps1](https://github.com/rubrikinc/PowerShell-Backup-Validation/blob/master/helper/generateCreds.ps1) file to create a starter set of credentials or see how the generation process works. 73 | 74 | _Note: Secure XML files can only be decrypted by the user account that created them._ 75 | 76 | ## Usage 77 | 78 | Once the Environment, Config, and Identity requirements are met, use the `Invoke-Build` function to execute a build. Here is a sample command using a splat. 79 | 80 | ```PowerShell 81 | $Splat = @{ 82 | File = '..\.build.ps1' 83 | EnvironmentFile = '.\environment\se-2.json' 84 | ConfigFile = '.\config\1-tier-app.json' 85 | IdentityPath = '.\credential\laptop' 86 | } 87 | 88 | Invoke-Build @Splat -Result Result 89 | ``` 90 | 91 | # :blue_book: Documentation 92 | 93 | Here are some resources to get you started! If you find any challenges from this project are not properly documented or are unclear, please [raise an issue](https://github.com/rubrikinc/use-case-powershell-backup-validation/issues/new/choose) and let us know! This is a fun, safe environment - don't worry if you're a GitHub newbie! :heart: 94 | 95 | * [Quick Start Guide](https://github.com/rubrikinc/Use-Case-PowerShell-Backup-Validation/blob/master/docs/quick-start.md) 96 | * [Rubrik SDK for Powershell Documentation](http://rubrikinc.github.io/rubrik-sdk-for-powershell/) 97 | * [Rubrik API Documentation](https://github.com/rubrikinc/api-documentation) 98 | * [VIDEO: Getting Started with the Backup Validation Use Case](https://www.youtube.com/watch?v=OCmFpno268M&feature=youtu.be) 99 | 100 | # :muscle: How You Can Help 101 | 102 | We glady welcome contributions from the community. From updating the documentation to adding more tests for this framework, all ideas are welcome. Thank you in advance for all of your issues, pull requests, and comments! :star: 103 | 104 | * [Contributing Guide](CONTRIBUTING.md) 105 | * [Code of Conduct](CODE_OF_CONDUCT.md) 106 | 107 | # :pushpin: License 108 | 109 | * [MIT License](LICENSE) 110 | 111 | # :point_right: About Rubrik Build 112 | 113 | We encourage all contributors to become members. We aim to grow an active, healthy community of contributors, reviewers, and code owners. Learn more in our [Welcome to the Rubrik Build Community](https://github.com/rubrikinc/welcome-to-rubrik-build) page. 114 | 115 | We'd love to hear from you! Email us: build@rubrik.com :love_letter: 116 | -------------------------------------------------------------------------------- /config/1-tier-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "virtualMachines": [ 3 | { 4 | "name": "SE-CWAHL-WIN", 5 | "mountName": "MOUNT-1TIER-APP", 6 | "guestCred": "guestCred.xml", 7 | "testIp": "172.17.50.111", 8 | "testNetwork": "VLAN50_Servers_Mount", 9 | "testGateway": "172.17.50.1", 10 | "testSubnet": "24", 11 | "tasks": ["Ping","Netlogon"] 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /config/2-tier-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "virtualMachines": [ 3 | { 4 | "name": "SE-CWAHL-WIN", 5 | "mountName": "MOUNT-2TIER-APP", 6 | "guestCred": "guestCred.xml", 7 | "testIp": "172.17.50.121", 8 | "testNetwork": "VLAN50_Servers_Mount", 9 | "testGateway": "172.17.50.1", 10 | "testSubnet": "24", 11 | "tasks": ["Ping","Netlogon"] 12 | }, 13 | { 14 | "name": "SE-CWAHL-WIN", 15 | "mountName": "MOUNT-2TIER-DB", 16 | "guestCred": "guestCred.xml", 17 | "testIp": "172.17.50.122", 18 | "testNetwork": "VLAN50_Servers_Mount", 19 | "testGateway": "172.17.50.1", 20 | "testSubnet": "24", 21 | "tasks": ["Ping","Netlogon"] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /config/3-tier-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "virtualMachines": [ 3 | { 4 | "name": "SE-CWAHL-WIN", 5 | "mountName": "MOUNT-3TIER-WEB", 6 | "guestCred": "guestCred.xml", 7 | "testIp": "172.17.50.131", 8 | "testNetwork": "VLAN50_Servers_Mount", 9 | "testGateway": "172.17.50.1", 10 | "testSubnet": "24", 11 | "tasks": ["Ping","Netlogon"] 12 | }, 13 | { 14 | "name": "SE-CWAHL-WIN", 15 | "mountName": "MOUNT-3TIER-APP", 16 | "guestCred": "guestCred.xml", 17 | "testIp": "172.17.50.132", 18 | "testNetwork": "VLAN50_Servers_Mount", 19 | "testGateway": "172.17.50.1", 20 | "testSubnet": "24", 21 | "tasks": ["Ping","Netlogon"] 22 | }, 23 | { 24 | "name": "SE-CWAHL-WIN", 25 | "mountName": "MOUNT-3TIER-DB", 26 | "guestCred": "guestCred.xml", 27 | "testIp": "172.17.50.133", 28 | "testNetwork": "VLAN50_Servers_Mount", 29 | "testGateway": "172.17.50.1", 30 | "testSubnet": "24", 31 | "tasks": ["Ping","Netlogon"] 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /docs/quick-start.md: -------------------------------------------------------------------------------- 1 | # Backup Validation with PowerShell 2 | This project is used to provide a framework for serial and parallel application testing against workloads that have been protected by Rubrik's Cloud Data Management platform. 3 | 4 | ## Abstract 5 | As virtual machines running in a VMware environment are backed up and cataloged by Rubrik, each backup or "snapshot" can be Live Mounted for testing and development purposes. This allows for an instant clone to be created that is hosted on the Rubrik filesystem layer and executed on an available ESXi host. A Live Mount takes up minimal space on the Rubrik filesystem because only incoming writes to the guest filesystem need be tracked. Because of this, the Live Mount technology is the perfect solution for quickly bringing up one or more virtual machines for validating the protected applications and services can be restored in the event of a production failure, outage, or disaster. 6 | 7 | ## Prerequisites 8 | 9 | * [PowerShell](https://aka.ms/getps6) 10 | * [Rubrik PowerShell Module](https://www.powershellgallery.com/packages/Rubrik/) 11 | * [Rubrik PowerShell Backup Validation Module](https://github.com/rubrikinc/rubrik-module-for-powershell-backup-validation) 12 | * [VMware PowerCLI](https://www.powershellgallery.com/packages/VMware.PowerCLI/) 13 | * [InvokeBuild](https://www.powershellgallery.com/packages/InvokeBuild/) 14 | 15 | ## Installation 16 | Once you have PowerShell installed on your system you can install the required modules by executing the following PowerShell commands: 17 | 18 | ``` 19 | Install-Module -Name Rubrik -Scope CurrentUser 20 | Install-Module -Name RubrikBackupValidation -Scope CurrentUser 21 | Install-Module -Name VMware.PowerCLI -Scope CurrentUser 22 | Install-Module -Name InvokeBuild -Scope CurrentUser 23 | ``` 24 | ![alt text](/img/image1.png) 25 | Sample output of Install-Module cmdlet 26 | 27 | This will install the modules in the current user scope and will not require local administrative privileges. If you would like to install the modules to be used for all users on a system the following commands can be executed: 28 | 29 | ``` 30 | Install-Module -Name Rubrik -Scope AllUsers 31 | Install-Module -Name RubrikBackupValidation -Scope AllUsers 32 | Install-Module -Name VMware.PowerCLI -Scope AllUsers 33 | Install-Module -Name InvokeBuild -Scope AllUsers 34 | ``` 35 | 36 | ## Verify installation 37 | The Rubrik PowerShell Backup Validation module, provides us with a function that can verify if we have all required modules properly installed, the `Test-PowerShellDependency` function: 38 | 39 | ``` 40 | Test-PowerShellDependency 41 | ``` 42 | ![alt text](/img/image10.png) 43 | 44 | Alternatively, we can use the `Get-Module` cmdlet to verify if all modules are successfully installed: 45 | 46 | ``` 47 | 'Rubrik', 'VMware.PowerCLI', 'InvokeBuild' | ForEach-Object { 48 | if (Get-Module -ListAvailable -Name $_) { 49 | '{0} module is successfully installed' -f $_ 50 | } else { 51 | '{0} module is not installed' -f $_ 52 | } 53 | } 54 | ``` 55 | 56 | ![alt text](/img/image2.png) 57 | The output shows that all modules are successfully installed on this system. 58 | 59 | ## Components 60 | In order to get started with Backup Validation there are several components that you need to understand. The PowerShell Modules that are required to run and connect to both vCenter cluster and Rubrik cluster, and the InvokeBuild module that will execute the build tasks. 61 | 62 | Then we will move on the different configuration and credential files and how these files tie into the backup validation process. 63 | 64 | Finally, we will look at the `.build.ps1` file, what it contains and how we can make additions to this. 65 | 66 | ### PowerShell Modules 67 | This use case leverages several PowerShell modules, outlined in this section. 68 | 69 | #### Rubrik SDK for PowerShell 70 | Rubrik’s API first architecture enables organizations to embrace and integrate Rubrik functionality into their existing automation processes. While Rubrik APIs can be consumed natively, companies are at various stages in their automation journey with different levels of automation knowledge on staff. The Rubrik SDK for PowerShell is a project that provides a Microsoft PowerShell module for managing and monitoring Rubrik's Cloud Data Management fabric by way of published RESTful APIs. 71 | 72 | #### Rubrik PowerShell Backup Validation 73 | This module has been created to help validate and verify the different components of a Backup Validation. It provides functions to generate the different Json files, allowing for this to be automated. Furthermore, it comes bundled with the `New-BuildConfiguration` function, that assists with the creation and validation of your backup validation scenario, by providing an interactive experience in which leads the administrator through the process of creating your first backup validation scenario. 74 | 75 | #### VMware PowerCLI 76 | VMware PowerCLI is a PowerShell module built by VMware. It provides a command-line and scripting tool with hundreds of cmdlets to manage and automate tasks using PowerShell. It is available in the PowerShell Gallery, which makes it easy to install and update. 77 | 78 | #### InvokeBuild 79 | Using the InvokeBuild framework, this project allows for an administrator to declare the topology of an application across one or more virtual machines. The entire collection of virtual machines is Live Mounted as a group and a battery of user-defined tests are applied. Upon the completion of the tests, the Live Mounts are removed, and a summary of results are displayed. 80 | 81 | ### Configuration 82 | There are three main points of configuration that we will use for Backup Validation which we will go over in details and showcase the example configuration that we have made available. 83 | 84 | * Environment JSON files 85 | * Config JSON files 86 | * Identity XML files 87 | 88 | ### Environment JSON Files 89 | The `Environment` folder contains JSON files that describe the Rubrik Cluster and vCenter Server information. This specifies either the IP address or the FQDN of both the Rubrik cluster and the vCenter cluster. It also specifies the credentials that will be used to connect to either cluster. 90 | 91 | A sample configuration looks like: 92 | 93 | ``` 94 | { 95 | "rubrikServer": "172.17.28.11", 96 | "rubrikCred": "rubrikCred.xml", 97 | "vmwareServer": "172.17.48.22", 98 | "vmwareCred": "vmwareCred.xml" 99 | } 100 | ``` 101 | 102 | ### Config JSON Files 103 | The `Config` folder contains JSON files that describe the virtual machines being tested. A sample configuration looks like: 104 | 105 | ``` 106 | { 107 | "virtualMachines": [ 108 | { 109 | "name": "SE-CWAHL-WIN", 110 | "mountName": "MOUNT-2TIER-APP", 111 | "guestCred": "guestCred.xml", 112 | "testIp": "172.17.50.121", 113 | "testNetwork": "VLAN50_Servers_Mount", 114 | "testGateway": "172.17.50.1", 115 | "tasks": ["Ping","Netlogon"] 116 | }, 117 | { 118 | "name": "SE-CWAHL-WIN", 119 | "mountName": "MOUNT-2TIER-DB", 120 | "guestCred": "guestCred.xml", 121 | "testIp": "172.17.50.122", 122 | "testNetwork": "VLAN50_Servers_Mount", 123 | "testGateway": "172.17.50.1", 124 | "tasks": ["Ping","Netlogon"] 125 | } 126 | ] 127 | } 128 | ``` 129 | 130 | #### Credentials 131 | The `Credentials` folder is not included in this repository. It can be placed anywhere in your environment and should host secure XML files created with `Export-Clixml` containing the credentials needed to communicate with the Rubrik cluster, vCenter Server, and any guest operating systems involved in the application testing. 132 | 133 | Use the [`generateCreds.ps1`](https://github.com/rubrikinc/PowerShell-Backup-Validation/blob/master/helper/generateCreds.ps1) file to create a starter set of credentials or see how the generation process works. The script takes a single argument for the Path parameter, which will determine where the files are stored for the three types of credentials will be stored. 134 | 135 | ``` 136 | param( 137 | $Path 138 | ) 139 | 140 | $CredType = @("rubrikCred.xml","vmwareCred.xml","guestCred.xml") 141 | 142 | foreach ($Type in $CredType) { 143 | $Credential = Get-Credential -Message $Type 144 | $Credential | Export-Clixml -Path ($Path + "\" + $Type) 145 | } 146 | ``` 147 | 148 | #### Note 149 | Secure XML files can only be decrypted by the user account that created them, therefore they cannot be used by other users. Anyone that wants to run the build validation will have to generate their own set of credentials. In the following example I attempt to decrypt my credentials with a different user account, which fails as expected: 150 | 151 | ![alt text](/img/image3.png) 152 | 153 | It is also important to note that these files can only be created on Windows systems. Both PowerShell and PowerShell Core support storing credentials on disk. This functionality is not available on other operating systems because `Export-Clixml` cannot be used to encrypt credentials as seen in the following screenshot running PowerShell Core on Ubuntu. 154 | 155 | ![alt text](/img/image4.png) 156 | 157 | ### Build Script 158 | The Build script is a script specifically written to be used in combination with the InvokeBuild module. There are a few concepts that are used by this module. 159 | 160 | #### Build Tasks 161 | Tasks can be defined in the `.build.ps1` script, this is a specific alias to the `Add-BuildTask` function of the InvokeBuild Module. A task is like a PowerShell function, with some differences. Tasks can refer to multiple other tasks, but each of those tasks will only be invoked once. 162 | 163 | An example of a simple task is the following which verifies the OS version by checking whether the OS is 32-bit or 64 bit: 164 | 165 | ``` 166 | task OSVersion { 167 | if ((wmic os get osarchitecture) -match '64-bit') { 168 | '64' 169 | } else { 170 | '32' 171 | } 172 | } 173 | ``` 174 | 175 | These tasks can be referenced in either multiple other tasks or can be called at the end of the build script. 176 | 177 | ##### Task Code Examples 178 | ``` 179 | task GetConfig { 180 | $script:Environment = Get-Content -Path $EnvironmentFile | ConvertFrom-Json 181 | $script:Config = Get-Content -Path $ConfigFile | ConvertFrom-Json 182 | # If a trailing backslash is omitted, this will make sure it's added to correct for future path + filename activities 183 | if ($IdentityPath.Substring($IdentityPath.Length - 1) -ne '\') { 184 | $script:IdentityPath += '\' 185 | } 186 | } 187 | ``` 188 | 189 | This task is used to load the different configuration files that will be used by the other build tasks that follow this command. 190 | 191 | ``` 192 | task ConnectVMware { 193 | $Credential = Import-Clixml -Path ($IdentityPath + $Environment.vmwareCred) 194 | $null = Connect-VIServer -Server $Environment.vmwareServer -Credential $Credential 195 | Write-Verbose -Message "VMware Status: Connected to $($global:DefaultVIServer.Name)" -Verbose 196 | } 197 | ``` 198 | 199 | The `ConnectVMware` task is used to connect to the VMware cluster and will provide verbose information when successfully connected. 200 | 201 | At the end of the script we will logically group together the different build tasks in 5 separate build tasks: 202 | 203 | * 1_Init 204 | * 2_Connect 205 | * 3_LiveMount 206 | * 4_LiveMountNetwork 207 | * 5_Testing 208 | 209 | ``` 210 | task 1_Init ` 211 | GetConfig 212 | 213 | task 2_Connect ` 214 | ConnectRubrik, 215 | ConnectVMware 216 | 217 | task 3_LiveMount ` 218 | CreateLiveMount, 219 | ValidateLiveMount, 220 | ValidateLiveMountTools 221 | 222 | task 4_LiveMountNetwork ` 223 | MoveLiveMountNetwork, 224 | MoveLiveMountNetworkAddress 225 | 226 | task 5_Testing ` 227 | LiveMountTest 228 | ``` 229 | 230 | The last line of the build script specific the tasks to be executed, the dot indicates the default build task: 231 | 232 | ``` 233 | task . ` 234 | 1_Init, 235 | 2_Connect, 236 | 3_LiveMount, 237 | 4_LiveMountNetwork, 238 | 5_Testing, 239 | Cleanup 240 | ``` 241 | 242 | This will run the tasks in the order they are specific, starting with the initialization of the configuration files and cleaning any remaining live mounts at the end of the validation. 243 | 244 | #### Tests 245 | A `tests.ps1` file is also included in the package, this contains the tests that will be available during the build process. Every in the Config JSON files we specify which test will run against which VM. 246 | 247 | An example of a test is the following ping test: 248 | 249 | ``` 250 | task Ping { 251 | assert (Test-Connection -ComputerName $Config.testIp -Quiet) "Unable to ping the server." 252 | } 253 | ``` 254 | 255 | The `tests.ps1` file can be updated with additional tests, and once these tests have been created they can be added to the Config JSON files for the relevant systems. 256 | 257 | ## Validate Backup 258 | Now that we have all components in place, we will create our own backup validation workflow. 259 | 260 | ### Prepare the Environment 261 | To get started we will download the Build Validation package and extract it to a folder named “Backup Validations”. The package is available in the [PowerShell-Backup-Validation](https://github.com/rubrikinc/Use-Case-PowerShell-Backup-Validation) repository. The zipped file is available for download [here](https://github.com/rubrikinc/PowerShell-Backup-Validation/archive/master.zip). 262 | 263 | #### Configure Environment Files 264 | In the Environment files make sure that the correct IP addresses or FQDNs are listed for both the Rubrik Cluster as well as the vCenter cluster. 265 | 266 | When specify the credentials, just the filename is required as we will specify the specific path when running the `Invoke-Build` function. 267 | 268 | #### Configure Config Files 269 | In the Config files a number of configuration options are available: 270 | 271 | * `Name` - the name of the VM 272 | * `mountName` - the name of the live mount 273 | * `guestCred` - the credentials file, only the filename is required, no path needs to be specified 274 | * `testIp` - IP Address configured for Live Mounted VM 275 | * `testNetwork` - virtual network used by the adapter of the Live Mount 276 | * `testGateway` - the gateway address of the network adapter 277 | * `tasks` - which tasks will run against this specific VM 278 | 279 | #### Create the Credential files 280 | In order to save the credential files to disk, we will first create a `credentials` folder: 281 | 282 | ``` 283 | mkdir ..\credentials 284 | ``` 285 | 286 | ![alt text](/img/image5.png) 287 | 288 | After that we will use the `generateCreds.ps1` to generate the credential files required to connect to the different environments. 289 | 290 | ![alt text](/img/image6.png) 291 | 292 | We can now verify that this command successfully created the files containing the encrypted credentials: 293 | 294 | ``` 295 | Get-ChildItem ..\credentials\ 296 | ``` 297 | ![alt text](/img/image7.png) 298 | 299 | ## Validate Backup with RubrikValidateBackup module 300 | 301 | This chapter will discuss how the the Rubrik Validate Backup Module can be used to help automate the creation of a new Backup Validation workflow. 302 | 303 | ### Prepare environment 304 | 305 | To get started we will download the Build Validation package and extract it to a folder named "Backup Validations". The package is available in the [Use-Case-PowerShell-Backup-Validation](https://github.com/rubrikinc/Use-Case-PowerShell-Backup-Validation) repository. The zipped file is available for download [here](https://github.com/rubrikinc/Use-Case-PowerShell-Backup-Validation/archive/master.zip). 306 | 307 | ### Interactive Configuration Generation 308 | 309 | Because the sheer amount of options that need to be configured it can be overwhelming to generate the first configuration. To assist with this process the `New-BuildConfiguration` function has been created. This will provide an interactive experience in which the configuration will not only be generated, but also provides the possibility to validate and run the first backup validation job. 310 | 311 | ``` 312 | New-BuildConfiguration 313 | ``` 314 | ![alt text](/img/image11.png) 315 | 316 | By typing either yes or no we get the option to view the most recent version of Quick Start guide for Backup Validation. 317 | ![alt text](/img/image12.png) 318 | 319 | The default browser will be used to open the website. 320 | 321 | ![alt text](/img/image13.png) 322 | 323 | In the following step we are requested to enter in the credentials for the Rubrik cluster, vCenter and the Guest OS. Since we are running this on Windows the credentials will be stored in encrypted xml files. If we run this same command on macOS the credentials will be stored in keychain. 324 | 325 | ![alt text](/img/image14.png) 326 | 327 | In the next step we will generate the `TestEnv.json` file, which contains the environment information, the IP address (or FQDN) of both the Rubrik Cluster and vCenter together with the corresponding credentials for authentication. 328 | 329 | Afterwards we get prompted if we want to create an additional environment file, this is an optional step that can be used in case you would like to define multiple different environments to run your backup validation tests against. 330 | 331 | ![alt text](/img/image15.png) 332 | 333 | Now we can define the configuration information for our VM, we are asked for several parameters to fill in. This information will be used to Live Mount the selected VM and configure and check the tasks that we select. Because Tasks is an array, it will continue prompting for more entries, it will take an empty entry as the end of the list. 334 | 335 | ![alt text](/img/image16.png) 336 | 337 | Now that we have created the Environment and Config json files and securely stored our credentials, we are prompted to have our configuration validated. 338 | 339 | ![alt text](/img/image17.png) 340 | 341 | The next prompt is to validate we have the required modules installed and available for backup validation to run. If you already validated this in an earlier step this can be skipped by answering no. 342 | 343 | ![alt text](/img/image18.png) 344 | 345 | In the last step we are asked to specify which files we would like to use to start the backup validation. If everything has correctly been filled in, you will be able to follow the process of the Backup Validation from the console. 346 | 347 | ### New-ConfigJson 348 | 349 | This function can be used to programmatically generate the config json files. The following parameters are used by this function: 350 | 351 | ![alt text](/img/image19.png) 352 | 353 | An example of how this can be used it the following: 354 | 355 | ``` 356 | Get-RubrikVM | Get-Random | ForEach-Object { 357 | $Splat = [ordered]@{ 358 | ConfigFilePath = "$($_.Name).json" 359 | Name = $_.Name 360 | MountName = 'LiveMnt-{0}' -f $_.Name 361 | GuestCred = 'GuestCred.xml' 362 | TestIp = '172.24.1.1' 363 | TestNetwork = 'ISOLATED_VLAN' 364 | TestGateway = '172.24.1.100' 365 | Tasks = 'Ping' 366 | } 367 | New-ConfigJson @Splat 368 | } 369 | ``` 370 | 371 | This will select a random virtual machine and create a `config.json` which can then be used for backup validation. 372 | 373 | ### Run `Invoke-Build` 374 | Once the Environment, Config, and Identity requirements are met, use the `Invoke-Build` function to execute a build. Here is a sample command using a PowerShell technique called splatting to store the parameters and arguments and execute `Invoke-Build`. 375 | 376 | ``` 377 | $Splat = @{ 378 | File = '.\.build.ps1' 379 | EnvironmentFile = '.\environment\testhost.json' 380 | ConfigFile = '.\config\TestConfiguration.json' 381 | IdentityPath = '.\credentials' 382 | } 383 | 384 | Invoke-Build @Splat -Result Result 385 | ``` 386 | 387 | ![alt text](/img/image8.png) 388 | 389 | When the build process is started we can see the Build Tasks are organized in the `1_Init/GetConfig` format, following the structure that was defined in `.build.ps1`. 390 | 391 | ![alt text](/img/image9.png) 392 | 393 | Upon completion of all the build tests we can see that all our build tests have successfully been completed and no further action must be taken. Our backup validation process has been completed. 394 | 395 | ## Further Reading 396 | This section contains links to sources of documentation and information that provide further information about the individual components. 397 | 398 | ### Rubrik SDK for PowerShell 399 | * [Rubrik SDK for PowerShell Documentation](http://rubrikinc.github.io/rubrik-sdk-for-powershell/) 400 | * [VIDEO: Getting Started with the Rubrik SDK for PowerShell](https://www.youtube.com/watch?v=tY6nQLNYRSE) 401 | * [Rubrik SDK for PowerShell](https://github.com/rubrikinc/rubrik-sdk-for-powershell) 402 | 403 | ### VMware PowerCLI 404 | * [VMware PowerCLI User Guide](https://vdc-download.vmware.com/vmwb-repository/dcr-public/2156d7ad-8f0f-4001-9de5-0cb95340873b/84fc3e8c-4755-4376-9917-18eb49a6bcdf/vmware-powercli-111-user-guide.pdf) 405 | 406 | ### `InvokeBuild` 407 | * [`Invoke-Build` Project Wiki](https://github.com/nightroman/Invoke-Build/wiki) 408 | -------------------------------------------------------------------------------- /environment/se-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "rubrikServer": "172.17.28.11", 3 | "rubrikCred": "rubrikCred.xml", 4 | "vmwareServer": "172.17.48.22", 5 | "vmwareCred": "vmwareCred.xml" 6 | } -------------------------------------------------------------------------------- /environment/se-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "rubrikServer": "172.17.28.15", 3 | "rubrikCred": "rubrikCred.xml", 4 | "vmwareServer": "172.17.48.22", 5 | "vmwareCred": "vmwareCred.xml" 6 | } -------------------------------------------------------------------------------- /helper/CodeSamples.ps1: -------------------------------------------------------------------------------- 1 | Test-PowerShellDependency 2 | 3 | New-BuildConfiguration 4 | 5 | -------------------------------------------------------------------------------- /helper/Show-BuildGraph.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | .Synopsis 4 | Shows Invoke-Build task graph using Graphviz. 5 | Invoke-Build - Build Automation in PowerShell 6 | Copyright (c) 2011-2017 Roman Kuzmin 7 | 8 | .Description 9 | Requires: 10 | - Graphviz: http://graphviz.org 11 | - Graphviz\bin in the environment variable Graphviz or in the path. 12 | - Invoke-Build in the script directory, in the path, or as the module. 13 | 14 | The script calls Invoke-Build.ps1 in order to get the tasks (WhatIf mode), 15 | builds the DOT file and calls Graphviz's dot.exe in order to visualize it 16 | using one of the supported output formats and the associated application. 17 | 18 | Tasks with code are shown as boxes, tasks without code are shown as ovals. 19 | Safe references are shown with dotted edges, regular calls are shown with 20 | solid edges. Call numbers on edges are not shown by default. 21 | 22 | EXAMPLES 23 | 24 | # Make and show a PDF for the default build script 25 | Show-BuildGraph 26 | 27 | # Make Build.png with call numbers and top to bottom edges 28 | Show-BuildGraph -Number -NoShow -Code '' -Output Build.png 29 | 30 | .Parameter File 31 | See: help Invoke-Build -Parameter File 32 | .Parameter Output 33 | The output file and the format specified by its extension. 34 | The default is "$env:TEMP\Graphviz-xxxxxxxx.pdf". 35 | .Parameter Code 36 | Custom DOT code added to the graph definition, see Graphviz manuals. 37 | The default 'graph [rankdir=LR]' tells edges to go from left to right. 38 | .Parameter Parameters 39 | Build script parameters needed in special cases when they alter tasks. 40 | .Parameter NoShow 41 | Tells to create the output file without showing it. 42 | In this case Output is normally specified by a caller. 43 | .Parameter Number 44 | Tells to show task job numbers. Jobs are tasks (numbers are shown on 45 | edges) and own scripts (numbers are shown in task boxes after names). 46 | 47 | .Link 48 | https://github.com/nightroman/Invoke-Build 49 | #> 50 | 51 | param( 52 | [Parameter(Position=0)] 53 | [string]$File, 54 | [Parameter(Position=1)] 55 | [string]$Output, 56 | [string]$Code = 'graph [rankdir=LR]', 57 | [hashtable]$Parameters, 58 | [switch]$NoShow, 59 | [switch]$Number 60 | ) 61 | 62 | trap {$PSCmdlet.ThrowTerminatingError($_)} 63 | $ErrorActionPreference = 'Stop' 64 | 65 | # resolve dot.exe 66 | $dot = if ($env:Graphviz) {"$env:Graphviz\dot.exe"} else {'dot.exe'} 67 | $dot = Get-Command $dot -CommandType Application -ErrorAction 0 68 | if (!$dot) {throw 'Cannot resolve dot.exe'} 69 | 70 | # resolve Invoke-Build 71 | $ib = "$(Split-Path $MyInvocation.MyCommand.Path)/Invoke-Build.ps1" 72 | if (!(Test-Path -LiteralPath $ib)) { 73 | $ib = Get-Command Invoke-Build -ErrorAction 0 74 | if (!$ib) {throw 'Cannot resolve Invoke-Build'} 75 | } 76 | 77 | # output 78 | if ($Output) { 79 | if (!($type = [System.IO.Path]::GetExtension($Output))) {throw 'Output must have an extension'} 80 | $Output = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Output) 81 | $type = $type.Substring(1).ToLower() 82 | } 83 | else { 84 | $hash = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($(if ($File) {$File} else {''})) 85 | $hash = '{0:x8}' -f ($hash.ToUpper().GetHashCode()) 86 | $Output = "$env:TEMP\Graphviz-$hash.pdf" 87 | $type = 'pdf' 88 | } 89 | 90 | # get tasks 91 | if (!$Parameters) {$Parameters = @{}} 92 | $all = & $ib ?? $File @Parameters 93 | 94 | # DOT code 95 | $text = @( 96 | 'digraph Tasks {' 97 | $Code 98 | foreach($it in $all.Values) { 99 | $name = $it.Name 100 | '"{0}"' -f $name 101 | $num = 0 102 | $script = '' 103 | foreach($job in $it.Jobs) { 104 | ++$num 105 | if ($job -is [string]) { 106 | $edge = ' ' 107 | if ($Number) { 108 | $edge += "label=$num " 109 | } 110 | if ($it.Safe -contains $job) { 111 | $edge += "style=dotted " 112 | } 113 | '"{0}" -> "{1}" [{2}]' -f $name, $job, $edge 114 | } 115 | else { 116 | $script += "{$num}" 117 | } 118 | } 119 | if ($script) { 120 | if ($Number) { 121 | '"{0}" [ shape=box label="{0} {1}" ]' -f $name, $script 122 | } 123 | else { 124 | '"{0}" [ shape=box ]' -f $name 125 | } 126 | } 127 | } 128 | '}' 129 | ) 130 | 131 | #! temp file UTF8 no BOM 132 | $temp = "$env:TEMP\Graphviz.dot" 133 | [System.IO.File]::WriteAllLines($temp, $text) 134 | 135 | # make 136 | & $dot "-T$type" -o $Output $temp 137 | if ($LastExitCode) {return} 138 | 139 | # show 140 | if ($NoShow) {return} 141 | Invoke-Item $Output 142 | -------------------------------------------------------------------------------- /helper/demoParallel.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [ValidateSet('Home', 'Laptop')] 3 | $Demo 4 | ) 5 | 6 | switch ($Demo) { 7 | # Demo on home workstation 8 | 'Home' { 9 | Invoke-Builds -Result Result @( 10 | @{ 11 | File = '..\.build.ps1'; 12 | EnvironmentFile = '.\environment\se-2.json'; 13 | ConfigFile = '.\config\2-tier-app.json'; 14 | IdentityPath = '.\credential\home' 15 | } 16 | @{ 17 | File = '..\.build.ps1'; 18 | EnvironmentFile = '.\environment\se-2.json'; 19 | ConfigFile = '.\config\3-tier-app.json'; 20 | IdentityPath = '.\credential\home' 21 | } 22 | ) 23 | } 24 | # Demo on laptop 25 | 'Laptop' { 26 | Invoke-Builds -Result Result @( 27 | @{ 28 | File = '..\.build.ps1'; 29 | EnvironmentFile = '.\environment\se-2.json'; 30 | ConfigFile = '.\config\1-tier-app.json'; 31 | IdentityPath = '.\credential\laptop' 32 | } 33 | @{ 34 | File = '..\.build.ps1'; 35 | EnvironmentFile = '.\environment\se-2.json'; 36 | ConfigFile = '.\config\2-tier-app.json'; 37 | IdentityPath = '.\credential\laptop' 38 | } 39 | ) 40 | } 41 | } 42 | 43 | return $Result.Tasks | Format-Table Elapsed, Name, Error -AutoSize -------------------------------------------------------------------------------- /helper/demoSerial.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [ValidateSet('Home1','Home2','Laptop')] 3 | $Demo 4 | ) 5 | 6 | switch ($Demo) { 7 | # Demo on home workstation 8 | 'Home1' { 9 | $Splat = @{ 10 | File = '..\.build.ps1' 11 | EnvironmentFile = '.\environment\se-2.json' 12 | ConfigFile = '.\config\1-tier-app.json' 13 | IdentityPath = '.\credential\home' 14 | } 15 | } 16 | 'Home2' { 17 | $Splat = @{ 18 | File = '..\.build.ps1' 19 | EnvironmentFile = '.\environment\se-2.json' 20 | ConfigFile = '.\config\3-tier-app.json' 21 | IdentityPath = '.\credential\home' 22 | } 23 | } 24 | # Demo on laptop 25 | 'Laptop' { 26 | $Splat = @{ 27 | File = '..\.build.ps1' 28 | EnvironmentFile = '.\environment\se-2.json' 29 | ConfigFile = '.\config\1-tier-app.json' 30 | IdentityPath = '.\credential\laptop' 31 | } 32 | } 33 | } 34 | 35 | Invoke-Build @Splat -Result Result 36 | 37 | return $Result.Tasks | Format-Table Elapsed, Name, Error -AutoSize -------------------------------------------------------------------------------- /helper/generateCreds.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Path 3 | ) 4 | 5 | $CredType = @("rubrikCred.xml","vmwareCred.xml","guestCred.xml") 6 | 7 | foreach ($Type in $CredType) { 8 | $Credential = Get-Credential -Message $Type 9 | $Credential | Export-Clixml -Path ($Path + "\" + $Type) 10 | } -------------------------------------------------------------------------------- /img/build-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/build-graph.png -------------------------------------------------------------------------------- /img/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image1.png -------------------------------------------------------------------------------- /img/image10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image10.png -------------------------------------------------------------------------------- /img/image11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image11.png -------------------------------------------------------------------------------- /img/image12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image12.png -------------------------------------------------------------------------------- /img/image13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image13.png -------------------------------------------------------------------------------- /img/image14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image14.png -------------------------------------------------------------------------------- /img/image15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image15.png -------------------------------------------------------------------------------- /img/image16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image16.png -------------------------------------------------------------------------------- /img/image17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image17.png -------------------------------------------------------------------------------- /img/image18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image18.png -------------------------------------------------------------------------------- /img/image19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image19.png -------------------------------------------------------------------------------- /img/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image2.png -------------------------------------------------------------------------------- /img/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image3.png -------------------------------------------------------------------------------- /img/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image4.png -------------------------------------------------------------------------------- /img/image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image5.png -------------------------------------------------------------------------------- /img/image6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image6.png -------------------------------------------------------------------------------- /img/image7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image7.png -------------------------------------------------------------------------------- /img/image8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image8.png -------------------------------------------------------------------------------- /img/image9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubrikinc/use-case-powershell-backup-validation/4512615a5c44fbb3287049154dcaf0f771861ff4/img/image9.png -------------------------------------------------------------------------------- /tests.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Config, 3 | [System.Management.Automation.PSCredential]$GuestCredential 4 | ) 5 | 6 | # 7 | # Tests running on the local machine, e. g. try to ping the test vm 8 | # 9 | task Ping { 10 | $progressPreference = 'SilentlyContinue'; 11 | Test-Connection $Config.testIp -Quiet 6> $null 12 | assert (Test-Connection $Config.testIp -Quiet 6> $null) "Unable to ping the server." 13 | } 14 | 15 | task Port80 { 16 | if ( $(Get-PSVersion).Major -ge 6 ) { 17 | # Test-NetConnection is not available on core non Windows, older versions of Windows do not contain test net-connection so implemented an additional fall back with .Net.Sockets. 18 | assert ( 19 | Test-Connection $Config.testIp -TCPPort 80 20 | ) "Unable to connect to port 80 of the server." 21 | } 22 | else { 23 | if (Get-Command -Name Test-NetConnection -ErrorAction SilentlyContinue) { 24 | assert ( 25 | Test-NetConnection $Config.testIp -Port 80 26 | ) "Unable to connect to port 80 of the server." 27 | } else { 28 | assert ( 29 | $(try {$socket = New-Object Net.Sockets.TcpClient($Config.testIp, 80);if ($socket.Connected) {$true};$socket.Close()} catch {}) 30 | ) "Unable to connect to port 80 of the server." 31 | } 32 | } 33 | } 34 | 35 | # 36 | # Tests running inside the test vm, e. g. check if a service is running. VMware-tools are used to achieve this. 37 | # 38 | task NTDS { 39 | $splat = @{ 40 | ScriptText = 'if ( Get-Service "NTDS" -ErrorAction SilentlyContinue ) { Write-Output "running" } else { Write-Output "not running" }' 41 | ScriptType = 'PowerShell' 42 | VM = $Config.mountName 43 | GuestCredential = $GuestCredential 44 | } 45 | $output = Invoke-VMScript @splat -ErrorAction Stop 46 | equals "$($output.Trim())" "running" 47 | } 48 | 49 | task MSSQLSERVER { 50 | $splat = @{ 51 | ScriptText = 'if ( Get-Service "MSSQLSERVER" -ErrorAction SilentlyContinue ) { Write-Output "running" } else { Write-Output "not running" }' 52 | ScriptType = 'PowerShell' 53 | VM = $Config.mountName 54 | GuestCredential = $GuestCredential 55 | } 56 | $output = Invoke-VMScript @splat -ErrorAction Stop 57 | equals "$($output.Trim())" "running" 58 | } 59 | task . --------------------------------------------------------------------------------