PowerRvB.psm1

function Invoke-PodClone {
    param(
        [Parameter(Mandatory)]
        [String] $SourceResourcePool,
        [Parameter(Mandatory)]
        [String] $Target,
        [Parameter(Mandatory)]
        [int] $Pods,
        [Parameter(Mandatory)]
        [String] $Tag,
        [Parameter(Mandatory)]
        [Boolean] $AssignPortGroups,
        [Parameter(Mandatory)]
        [int] $FirstPodNumber,
        [Boolean] $CompetitionSetup=$True,
        [Boolean] $CreateUsers,
        [String] $Domain,
        [String] $Role,
        [Boolean] $CreateRouters,
        [String] $WanPortGroup='0010_DefaultNetwork'
    )

    try {
        Get-Tag -Name $Tag -ErrorAction Stop| Out-Null
    }
    catch {
        New-TagCategory -Name $Tag -Description $tag -EntityType VApp, DistributedPortGroup, VM | Out-Null
        New-Tag -Name $Tag -Category (Get-TagCategory -Name $Tag) | Out-Null
    }

    $CreatedPortGroups = New-PodPortGroups -Portgroups $Pods -StartPort $FirstPodNumber -EndPort ($FirstPodNumber + $Pods + 20) -Tag $Tag -AssignPortGroups $AssignPortGroups

    for ($i = 0; $i -lt $Pods; $i++) {
        Write-Host 'Creating' ( -join ($CreatedPortGroups[$i], '_Pod...'))
        New-VApp -Name ( -join ($CreatedPortGroups[$i], '_Pod')) -Location (Get-ResourcePool -Name $Target -ErrorAction Stop) -ErrorAction Stop | New-TagAssignment -Tag $Tag | Out-Null
    }

    $VMsToClone = Get-ResourcePool -Name $SourceResourcePool | Get-VM

    $VMsToClone | ForEach-Object {
        if (Get-Snapshot -VM $_ | Where-Object name -eq SnapshotForCloning) {
            $_ | Get-Snapshot -Name SnapshotForCloning | Remove-Snapshot -Confirm:$false
        }
        New-Snapshot -VM $_ -Name SnapshotForCloning
    }

    foreach ($VM in $VMsToClone) {
        Get-VApp -Tag $Tag | ForEach-Object { New-VM -VM $VM -Name ( -join (($_.Name.Split("P")[0]), $VM.Name)) -ResourcePool (Get-VApp -Name ($_.Name)).Name -LinkedClone -ReferenceSnapshot "SnapshotForCloning" -RunAsync }
    }

    $CloningCompletion = $false
    While (!$CloningCompletion) {
        $testNetAdapter = Get-VApp -Tag $Tag | Get-VM | Get-NetworkAdapter
        if ($testNetAdapter.Count -eq (($VMsToClone.Count) * $Pods)) {
            $CloningCompletion = $true
            Write-Progress -Activity "Cloning in Progress..." -Status "percent complete: 100" -PercentComplete 100
        } else {
            $CloningCompletion = $false
            $PercentComplete = [math]::Round(($testNetAdapter.Count / (($VMsToClone.Count) * $Pods)) * 100, 2)
            Write-Progress -Activity "Cloning in Progress..." -Status "percent complete: $PercentComplete" -PercentComplete $PercentComplete
            Start-Sleep 2
        }
    }

    if ($CreateRouters -eq $true) {
        Write-Host 'Creating the pod routers...'
        if ($CompetitionSetup -eq $True) {
            for ($i = 0; $i -lt $Pods; $i++) {
                Write-Host 'Creating' ( -join ($CreatedPortGroups[$i], '_Pod Router...'))
                New-PodRouter -Target ( -join ($CreatedPortGroups[$i], '_Pod')) -WanPortGroup $WanPortGroup -LanPortGroup ( -join ($CreatedPortGroups[$i], '_PodNetwork')) -PFSenseTemplate '1:1NAT_PodRouter' -ErrorAction Stop | Out-Null
            }
        } else {
            for ($i = 0; $i -lt $Pods; $i++) {
                Write-Host 'Creating' ( -join ($CreatedPortGroups[$i], '_Pod Router...'))
                New-PodRouter -Target ( -join ($CreatedPortGroups[$i], '_Pod')) -WanPortGroup $WanPortGroup -LanPortGroup ( -join ($CreatedPortGroups[$i], '_PodNetwork')) -PFSenseTemplate 'pfSense Blank' | Out-Null
            }
        }
    }

    if ($AssignPortGroups) {
        #Set Variables
        $CloningCompletion = $true
        $Routers = Get-VApp -Tag $Tag -ErrorAction Stop | Get-VM | Where-Object -Property Name -Like '*PodRouter*'
        $VMs = Get-VApp -Tag $Tag -ErrorAction Stop | Get-VM | Where-Object -Property Name -NotLike '*PodRouter*'
                    
        #Set VM Port Groups
        if ($VMs) {
            $VMs | 
                ForEach-Object { 
                    Get-NetworkAdapter -VM $_ -Name "Network adapter 1" -ErrorAction Stop | 
                        Set-NetworkAdapter -Portgroup (Get-VDPortGroup -name ( -join ($_.Name.Split("_")[0], '_PodNetwork'))) -Confirm:$false -RunAsync | Out-Null 
                }
        }
        #Set Router Port Groups
        $Routers | 
            ForEach-Object {
            Get-NetworkAdapter -VM $_ -Name "Network adapter 1" -ErrorAction Stop | 
                Set-NetworkAdapter -Portgroup (Get-VDPortgroup -Name $WanPortGroup) -Confirm:$false -RunAsync | Out-Null 
            Get-NetworkAdapter -VM $_ -Name "Network adapter 2" -ErrorAction Stop | 
                Set-NetworkAdapter -Portgroup (Get-VDPortGroup -name ( -join ($_.Name.Split("_")[0], '_PodNetwork'))) -Confirm:$false -RunAsync | Out-Null 
            }
    }
    
    
    
    $names = @()
    if ($CreateUsers -eq $true) {
        foreach ($name in $CreatedPortGroups) {   
            $names += ( -join ($name, '_Pod'))
        }
        Write-Host 'Creating the pod users...'
        New-PodUsers -Pods $names -Role $Role -Description $Tag -Domain  | Out-Null
    }
    <#
        .SYNOPSIS
        Clones the given vSphere vApp a specified number of times.
 
        .PARAMETER Template
        Specifies the vApp template to be cloned.
 
        .PARAMETER Target
        Specifies the resource pool the pods will be cloned to.
         
        .PARAMETER Pods
        Specifies the number of pods to be cloned.
 
        .PARAMETER Tag
        Specifies the tag applied to the vApps, Port Groups, and Users.
 
        .PARAMETER FirstPortGroup
        Specifies the first port group Invoke-PodClone will check for port groups.
 
        .PARAMETER CreateUsers
        Specify if users will also be created for their respective pods.
         
        .PARAMETER Role
        Specify the vSphere role new users will be given.
 
        .PARAMETER CreateRouters
        Specify if pod routers will be created.
 
        .INPUTS
        None. You cannot pipe objects to Invoke-PodClone.
 
        .OUTPUTS
        Invoke-PodClone does not return any output.
 
        .EXAMPLE
        PS> Invoke-PodClone -Template EvansTemplate -Target 02-07_Evan -Pods 20 -Tag 'RvB Tag' -FirstPortGroup 1230 -CreateUsers $true -Role 01_RvBDirector -CreateRouters $true
 
        .LINK
        PowerRvB's Github Repository: https://github.com/cpp-swift/PowerRvB
    #>

    
}

# Password generation function
function Get-RandomPassword {
    param (
        [Parameter(Mandatory)]
        [ValidateRange(4, [int]::MaxValue)]
        [int] $length,
        [int] $upper = 1,
        [int] $lower = 1,
        [int] $numeric = 1,
        [int] $special = 1
    )
    if ($upper + $lower + $numeric + $special -gt $length) {
        throw "number of upper/lower/numeric/special char must be lower or equal to length"
    }
    $uCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    $lCharSet = "abcdefghijklmnopqrstuvwxyz"
    $nCharSet = "0123456789"
    $sCharSet = "/*-+,!?=()@;:._"
    $charSet = ""
    if ($upper -gt 0) { $charSet += $uCharSet }
    if ($lower -gt 0) { $charSet += $lCharSet }
    if ($numeric -gt 0) { $charSet += $nCharSet }
    if ($special -gt 0) { $charSet += $sCharSet }
    
    $charSet = $charSet.ToCharArray()
    $rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
    $bytes = New-Object byte[]($length)
    $rng.GetBytes($bytes)
 
    $result = New-Object char[]($length)
    for ($i = 0 ; $i -lt $length ; $i++) {
        $result[$i] = $charSet[$bytes[$i] % $charSet.Length]
    }
    $password = (-join $result)
    $valid = $true
    if ($upper -gt ($password.ToCharArray() | Where-Object { $_ -cin $uCharSet.ToCharArray() }).Count) { $valid = $false }
    if ($lower -gt ($password.ToCharArray() | Where-Object { $_ -cin $lCharSet.ToCharArray() }).Count) { $valid = $false }
    if ($numeric -gt ($password.ToCharArray() | Where-Object { $_ -cin $nCharSet.ToCharArray() }).Count) { $valid = $false }
    if ($special -gt ($password.ToCharArray() | Where-Object { $_ -cin $sCharSet.ToCharArray() }).Count) { $valid = $false }
 
    if (!$valid) {
        $password = Get-RandomPassword $length $upper $lower $numeric $special
    }
    return $password
}

function New-DevPod {

    param(
        [Parameter(Mandatory = $true)]
        [String] $Name,
        [Parameter(Mandatory = $true)]
        [String] $Target,
        [Boolean] $CreateRouter,
        [String] $WanPortGroup
    )

    # Creates the Dev Port Group, vApp, and Router
    $DevPortGroup = New-PodPortGroups -Portgroups 1 -StartPort 1300 -EndPort 1350 -AssignPortGroups $true
    New-VApp -Location $Target -Name $Name | Out-Null
    if ($CreateRouter -eq $true) {
        New-PodRouter -Target $Name -WanPortGroup $WanPortGroup -LanPortGroup ( -join ($DevPortGroup[0], '_PodNetwork')) -PFSenseTemplate "pfSense Blank" -ErrorAction Stop| Out-Null
    }

    $Templates = Get-Template | Sort-Object

    foreach ($template in $Templates) {
        Write-Host $Templates.IndexOf($template)'-' "$template" `n
    }

    $boxes = (Read-Host "Enter the boxes to be created in this Developer Pod (Ex: 0, 2, 1). Press enter to continue").split(',')
    $boxes = $boxes | ForEach-Object { $_ -Replace '\s', '' }

    if ($Boxes) {
        for ($i = 0; $i -ile $Boxes.Count; $i++) {
            New-VM -Name $Templates.Get($Boxes[$i].ToString()).Name `
                -ResourcePool (Get-VApp -Name ($Name)).Name `
                -Datastore (Get-DataStore -Name Ursula) `
                -Template $Templates.get($Boxes[$i].ToString()) -RunAsync -ErrorAction Stop | Out-Null
        }
        
        Write-Host 'IMPORTANT: Do not continue until all VMs are created. Press any key to continue...' -ForegroundColor Red
        $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
    
        for ($i = 0; $i -lt $Boxes.Count; $i++) {
            Get-ResourcePool (Get-VApp -Name ($Name)).Name -ErrorAction Stop | Get-VM | Where-Object -Property Name -NotLike '*PodRouter*' | Get-NetworkAdapter -Name "Network adapter 1" | Set-NetworkAdapter -Portgroup (Get-VDPortGroup -Name ( -join ($DevPortGroup[0], '_PodNetwork'))) -Confirm:$false -RunAsync | Out-Null
        }
    }

    
    <#
        .SYNOPSIS
        Creates a Development vApp with the desired virtual machines and functional networking.
 
        .PARAMETER Name
        Specifies the name of the vApp to be created.
 
        .PARAMETER Target
        Specifies the resource pool the vApp will be created in.
         
        .PARAMETER CreateRouter
        Specifies if pod router will be created.
 
        .PARAMETER WanPortGroup
        Specifies the WAN port used on the router.
 
        .INPUTS
        None. You cannot pipe objects to New-DevPod.
 
        .OUTPUTS
        New-DevPod does not return any output.
 
        .EXAMPLE
        PS> New-DevPod -Name EvansDevPod3 -Target 02-07_Evan -CreateRouter $true -WanPortGroup 0010_DefaultNetwork
 
        .LINK
        PowerRvB's Github Repository: https://github.com/cpp-swift/PowerRvB
    #>

}

function New-PodPortGroups {

    param(
        [ValidateRange(1, 100)]
        [Parameter(Mandatory = $true)]
        [int] $Portgroups,
        [ValidateRange(1000, 2000)]
        [Parameter(Mandatory = $true)]
        [int] $StartPort,
        [ValidateRange(1000, 2000)]
        [Parameter(Mandatory = $true)]
        [int] $EndPort,
        [String] $Tag,
        [Boolean] $AssignPortGroups

    )

    $ErrorActionPreference = "Stop"

    # Gets the list of existing port groups in the range
    $PortGroupList = Get-VDPortgroup -VDSwitch Main_DSW | Select-Object -ExpandProperty name | Sort-Object
    $PortGroupList = $PortGroupList | 
    ForEach-Object {
        [int]$PortGroupList[$PortGroupList.indexOf($_)].Substring(0, $PortGroupList[$PortGroupList.indexOf($_)].indexOf('_'))
    }
    $PortGroupList = $PortGroupList.where{ $_ -IN $StartPort..$EndPort }
    # Check if Port Groups can be created
    if ($EndPort - $StartPort - $PortGroupList.Count + 1 -lt $Portgroups) {
        $temp = $EndPort - $StartPort - $PortGroupList.Count + 1
        Write-Error -Message "There are not enough port groups available in this range. Only $temp can be created."
    }

    # Creates the port groups
    $j = $StartPort
    $i = 0
    While ($i -le $Portgroups - 1) {
        if ($PortGroupList.IndexOf($j) -ne -1) { $j++; continue }
        if ($j -gt $EndPort) { Write-Error -Message "There are no more available port groups in the specified range." }
        else {
            if ($AssignPortGroups) {
                Write-Host "Creating Port Group $j..."
                New-VDPortgroup -VDSwitch Main_DSW -Name ( -join ($j, '_PodNetwork')) -VlanId $j | Out-Null
                if ($Tag) {
                    Get-VDPortGroup -Name ( -join ($j, '_PodNetwork')) | New-TagAssignment -Tag (Get-Tag -Name $Tag) | Out-Null
                }
            }
            $j
            $j++
            $i++
        }
    }
    return $CreatedPortGroups

    <#
        .SYNOPSIS
        Creates a specified number of port groups within the given range.
 
        .DESCRIPTION
        Finds the first available port group and every subsequent available port group in the range until a specified amount of port groups are created.
        Errors out if the range does not have enough available port groups.
 
        .PARAMETER PortGroups
        Specifies the number of port groups to be created.
 
        .PARAMETER StartPort
        Specifies the first usable port.
         
        .PARAMETER EndPort
        Specifies the last usable port.
 
        .PARAMETER Tag
        Specifies the Tag applied to the port groups.
 
        .INPUTS
        None. You cannot pipe objects to New-PodPortGroups.
 
        .OUTPUTS
        Int32 Array. New-PodPortGroups returns an array of the VLAN IDs used by the created port groups.
 
        .EXAMPLE
        PS> New-PodPortGroups -PortGroups 10 -StartPort 1200 -EndPort 1300
 
        .LINK
        PowerRvB's Github Repository: https://github.com/cpp-swift/PowerRvB
    #>

}

# Creates a pfSense Router for the vApp
function New-PodRouter {

    param (
        [Parameter(Mandatory = $true)]
        [String] $Target,
        [Parameter(Mandatory = $true)]
        [String] $WanPortGroup,
        [Parameter(Mandatory = $true)]
        [String] $LanPortGroup,
        [Parameter(Mandatory = $true)]
        [String] $PFSenseTemplate

    )

    # Creating the Router
    $name = ( -join ($LanPortGroup.Substring(0, 4), '_PodRouter'))
    New-VM -Name $name `
        -ResourcePool (Get-Vapp -Name $Target).Name `
        -Datastore (Get-DataStore -Name Ursula) `
        -Template (Get-Template -Name $PFSenseTemplate) | Out-Null

    Get-VM -Name $name | Start-VM
    
    $SSHTest = $True
    if ($PFSenseTemplate -eq '1:1NAT_PodRouter') {
        Start-Sleep 30 
        while ($SSHTest) {
                $ThirdOctet = $LanPortGroup.Substring(2, 2)
                if ($ThirdOctet.StartsWith('0')) {
                    $ThirdOctet = $ThirdOctet.Substring(1,1)
                }
                $commands = "sed 's/172.16.254/172.16.$ThirdOctet/g' /cf/conf/config.xml > tempconf.xml; cp tempconf.xml /cf/conf/config.xml; rm /tmp/config.cache; /etc/rc.reload_all start"
                ssh "admin@172.16.254.1" $commands -ErrorAction continue
                $SSHTest = $false
            }
        }
    

    # Assigning port groups to the interfaces
   
    <#
        .SYNOPSIS
        Creates a router in a specified vApp.
 
        .DESCRIPTION
        Creates a router with a specified WAN port group and a specified LAN port group.
 
        .PARAMETER Target
        Specifies the vApp the Router will be created in.
 
        .PARAMETER WanPortGroup
        Specifies the WAN port group.
         
        .PARAMETER LanPortGroup
        Specifies the LAN port group.
 
        .INPUTS
        None. You cannot pipe objects to New-PodRouter.
 
        .OUTPUTS
        New-PodRouter does not return any output.
 
        .EXAMPLE
        PS> New-PodRouter -Target EvansvApp -WanPortgroup 0010_DefaultNetwork -LanPortgroup 1200_PodNetwork
 
        .LINK
        PowerRvB's Github Repository: https://github.com/cpp-swift/PowerRvB
    #>

} 

function New-PodUsers {

    param(
        [Parameter(Mandatory = $true)]
        [String[]] $Pods,
        [Parameter(Mandatory = $true)]
        [String] $Role,
        [Parameter(Mandatory = $true)]
        [String] $Description,
        [Parameter(Mandatory = $true)]
        [String] $Domain
    )

    # Creating the User Accounts
    Import-Module ActiveDirectory
    $file = ( -join ("$env:USERPROFILE\Desktop\", $Description , "Users.csv"))
    ForEach ($Pod in $Pods) {
        $Password = Get-RandomPassword 12 1 1 1 1
        $Name = ( -join ($Pod, 'User'))
        $SecurePassword = ConvertTo-SecureString -AsPlainText $Password -Force
        Write-Host 'Creating user' $Name
        New-ADUser -Name $Name -ChangePasswordAtLogon $false -AccountPassword $SecurePassword -Enabled $true -Description $Description -UserPrincipalName (-join ($Name, '@', $Domain))| Out-Null
        Add-ADGroupMember -Identity 'RvB Competitors' -Members $Name

        # Creating the Roles Assignments on vSphere
        New-VIPermission -Role (Get-VIRole -Name $Role -ErrorAction Stop) -Entity (Get-VApp -Name $Pod) -Principal ($Domain.Split(".")[0] + '\' + $Name) | Out-Null
        
        #Append User to CSV
        $out = "$Name,$Password"
        $out | Out-File $file -Append -Force
    }

    <#
        .SYNOPSIS
        Creates AD Users that correspond to specified pods and assigns them a specified vSphere role.
 
        .PARAMETER Pods
        Specifies the pods to create users for.
 
        .PARAMETER Role
        Specifies the vSphere role to be applied.
         
        .PARAMETER Description
        Specifies a description that is given to the users in AD.
 
        .INPUTS
        None. You cannot pipe objects to New-PodUsers.
 
        .OUTPUTS
        CSV File. On the cmdlet user's desktop, a CSV of usernames and passwords is generated.
 
        .EXAMPLE
        PS> New-PodUsers -Pods '1200_Pod','1201_Pod' -Role 01_RvBCompetitors -Description 'RvB Tag'
 
        .LINK
        PowerRvB's Github Repository: https://github.com/cpp-swift/PowerRvB
    #>

}

function Invoke-RvByeBye {
    
    param(
        [Parameter(Mandatory)]
        [String] $Tag
    )
    Get-VApp -Tag $Tag | Get-VM | Stop-VM -Confirm:$false | Out-Null
    Get-VApp -Tag $Tag | Get-VM | Remove-VM -DeletePermanently | Out-Null
    Get-Vapp -Tag $Tag | Remove-VApp -DeletePermanently | Out-Null
    Get-VDPortgroup -Tag $Tag -ErrorAction Stop | Remove-VDPortGroup -ErrorAction Stop | Out-Null
    Get-ADUser -Filter { Description -eq $Tag } | Remove-ADUser -ErrorAction Stop | Out-Null
    Get-TagCategory -Name $Tag -ErrorAction Stop | Remove-TagCategory -ErrorAction Stop | Out-Null

    <#
        .SYNOPSIS
        Removes all resources with a specified tag.
 
        .PARAMETER Tag
        Specifies the tag to be used.
 
        .INPUTS
        None. You cannot pipe objects to Invoke-RvByeBye.
 
        .OUTPUTS
        Invoke-RvByeBye does not return any output.
         
        .EXAMPLE
        PS> Invoke-RvByeBye -Tag 'RvB Tag'
 
        .LINK
        PowerRvB's Github Repository: https://github.com/cpp-swift/PowerRvB
    #>

}

function Invoke-ConfigureVMs {
    param (
        [cmdletbinding(SupportsShouldProcess)]
        [Parameter(Mandatory,ValueFromPipeline=$true)]
        [Object] $VM,
        [string] $NewTag=$null,
        [string] $Name,
        [int] $NewPortGroup=$null,
        [int] $NumCpu=$null,
        [int] $MemoryGB=$null,
        [Boolean] $CpuHotAddEnabled,
        [Boolean] $CpuHotRemoveEnabled,
        [Boolean] $MemoryHotAddEnabled

        
    )

    begin {

        if($NewPortGroup) {
            try {
                Get-VDPortgroup -Name ( -join ($NewPortGroup, '_PodNetwork')) -ErrorAction Stop
            }
            catch {
                New-VDPortgroup -VDSwitch Main_DSW -Name ( -join ($NewPortGroup, '_PodNetwork')) -VlanId $NewPortGroup | Out-Null
                Get-VDPortGroup -Name ( -join ($NewPortGroup, '_PodNetwork')) | New-TagAssignment -Tag (Get-Tag -Name $NewTag)
            }
        }
    }

    process {
        #$ErrorActionPreference = 'SilentlyContinue'
        if ($NewTag) { $VM | New-TagAssignment -Tag (Get-Tag -Name $NewTag)}
        if ($NewPortGroup) { $VM | Get-NetworkAdapter -Name "Network adapter 1" | Set-NetworkAdapter -Portgroup (Get-VDPortgroup -Name ( -join ($NewPortGroup, "_PodNetwork"))) -Confirm:$false -RunAsync | Out-Null }
        if ($NumCpu) { $VM | Set-VM -NumCpu $NumCpu -Confirm:$false -RunAsync }
        if ($MemoryGB) { $VM | Set-VM -MemoryGB $MemoryGB -Confirm:$false -RunAsync }
        if ($Name) { $VM | Set-VM -Name $Name -RunAsync -Confirm:$false }
        if ($MemoryHotAddEnabled) {$VM | Stop-VM -Confirm:$false | Set-VM -MemoryHotAddEnabled $MemoryHotAddEnabled -Confirm:$false -RunAsync }
        if ($CpuHotAddEnabled) {$VM | Stop-VM -Confirm:$false | Set-VM -CpuHotAddEnabled $CpuHotAddEnabled -Confirm:$false -RunAsync }
        if ($CpuHotRemoveEnabled) { $VM | Stop-VM -Confirm:$false | Set-VM -CpuHotRemoveEnabled $CpuHotRemoveEnabled -Confirm:$false -RunAsync }
    }
}