templates/run.ps1

# Input bindings are passed in via param block.
param($Timer)

# Get the current universal time in the default string format.
$currentUTCtime = (Get-Date).ToUniversalTime()

# The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.
if ($Timer.IsPastDue) {
    Write-Host "PowerShell timer is running late!"
}

# Write an information log with the current time.
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"


# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

$AzContext = Get-AzContext
$KeyVaultName = $env:APPSETTING_KEYVAULT_NAME
$SubscriptionId = $AzContext.Subscription.Id
$AVSResourceGroupName = $env:APPSETTING_AVS_RESOURCE_GROUP
$AVSPrivateCloudName = $env:APPSETTING_AVS_CLOUD_NAME

function Connect-CBSArray {
    param (
        [String] $KeyVaultName
    )
    $CBSArrays = @()
    Import-Module -Name PureStoragePowerShellSDK2
    $Secrets = Get-AzKeyVaultSecret -VaultName $KeyVaultName | where-object {$_.Name -like "*-$($KeyVaultName)-username"}
    foreach ($Secret in $Secrets) {
        $ArrayName = ($Secret.name -Split "-$($KeyVaultName)-username")[0]
        $CBSUsername = Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name $Secret.Name -AsPlainText
        #TODO: Password we got here is plain text. Any security concern?
        $CBSPassword = Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name "$($ArrayName)-$($KeyVaultName)-password" -AsPlainText
        $SecureCBSPassword = ConvertTo-SecureString -String $CBSPassword -AsPlainText -Force
        $Credential = New-Object System.Management.Automation.PSCredential -ArgumentList ($CBSUsername, $SecureCBSPassword)

        # If the string matches the format like "172-168-1-0", the array ip addressed was processed because secret name does not allow "."
        if ($ArrayName -match "^\d+-\d+-\d+-\d+$") {
            $ArrayName = $ArrayName.Replace("-", ".")
        }
        # TODO: How do we deal with the array failed to connect?
        $Array = Connect-Pfa2Array -EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError

        $CBSArrays += $Array
    }

    return $CBSArrays
}

function Get-AVSvCenterEndpoint {
    param (
        [Hashtable] $AuthHeader,
        [String] $SubscriptionId,
        [String] $AvsResourceGroupName,
        [String] $AvsPrivateCloudName
    )
    $RestUri = "https://management.azure.com/subscriptions/$($subscriptionId)/resourceGroups/$($avsResourceGroupName)/providers/Microsoft.AVS/privateClouds/$($avsPrivateCloudName)?api-version=2022-05-01"
    $Response = Invoke-RestMethod -Uri $restUri -Method Get -Headers $authHeader
    $VcsaEndpoint = $response.properties.endpoints.vcsa
    $VcsaIPAddress = [System.Uri]::new($vcsaEndpoint).Host

    return $vcsaIPAddress
}

function Get-AVSvCenterCredential {
    param (
        [Hashtable] $AuthHeader,
        [String] $SubscriptionId,
        [String] $AvsResourceGroupName,
        [String] $AvsPrivateCloudName
    )
    $RestUri = "https://management.azure.com/subscriptions/$($subscriptionId)/resourceGroups/$($avsResourceGroupName)/providers/Microsoft.AVS/privateClouds/$($avsPrivateCloudName)/listAdminCredentials?api-version=2022-05-01"
    $Response = Invoke-RestMethod -Uri $restUri -Method Post -Headers $AuthHeader

    return $Response
}

function Connect-AVSvCenter {
    param (
        [String] $AVSvCenterEndpoint,
        [String] $AVSvCenterUsername,
        [SecureString] $AVSvCenterPassword
    )
    Set-PowerCLIConfiguration -InvalidCertificateAction:Ignore -DefaultVIServerMode multiple -Confirm:$false | Out-Null
    $Credential = New-Object System.Management.Automation.PSCredential -ArgumentList ($AVSvCenterUsername, $AVSvCenterPassword)
    $vCenterServer = Connect-VIserver -server $AVSvCenterEndpoint -Credential $Credential
    return $vCenterServer
}

function Get-AzureAuthHeader {
    param (
        $AzContext
    )
    $AzProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
    $ProfileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($AzProfile)
    $Token = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId)
    $AuthHeader = @{
        'Content-Type'='application/json'
        'Authorization'='Bearer ' + $token.AccessToken
    }

    return $AuthHeader
}

function Refresh-PCBSCluster {
    param (
        [String] $AvsResourceGroupName,
        [String] $AvsPrivateCloudName,
        $vCenterServer,
        $CBSArrays
    )
    $Clusters = Get-Cluster -Server $vCenterServer
    $ArrayIscsiAddressMap = @{}

    foreach ($CBSArray in $CBSArrays) {
        $EthList = (Get-Pfa2NetworkInterface  -Array $CBSArray | Where-Object {$_.services -eq "iscsi"} | Where-Object {$_.enabled -eq $true} | Where-Object {$null -ne $_.Eth.address}).Eth
        $ISCSIAddressList = @()
        foreach ($Eth in $EthList) {
          $ISCSIAddressList += $eth.address
        }
        $ArrayIscsiAddressMap[$CBSArray] = $iSCSIAddressList
    }

    foreach ($Cluster in $Clusters) {
        # Build cluster only when there is a matched iscsi address
        foreach ($Array in $ArrayIscsiAddressMap.Keys) {
            Write-Host "iscsi $($ArrayIscsiAddressMap[$Array])"

            $MatchedISCSIAddress = $Cluster | Get-VMHost -Server $vCenterServer | Get-VMHostHba -Server $vCenterServer -Type iScsi | Get-IScsiHbaTarget -Server $vCenterServer | Where-Object {($_.Type -eq "Static") -and ($ArrayIscsiAddressMap[$Array] -contains $_.Address)}
            $ArrayName = (Get-Pfa2Array -Array $Array).Name
            if ($MatchedISCSIAddress) {
                Write-Host "Connection between CBS $ArrayName and cluster $($Cluster.Name) is found. Matched iSCSI address is $MatchedISCSIAddress. Start building cluster..."
                Build-PCBSCluster -ClusterName $Cluster.Name -AVSCloudName $AVSPrivateCloudName -AVSResourceGroup $AVSResourceGroupName -PureCloudBlockStoreConnection $Array
            } else {
                Write-Host "Connection between CBS $ArrayName and cluster $($Cluster.Name) is not found. Skip the array for the cluster"
            }
        }
    }
}

$CBSArrays = Connect-CBSArray -KeyVaultName $KeyVaultName

$AuthHeader = Get-AzureAuthHeader -AzContext $AzContext
$AVSvCenterCredential = Get-AVSvCenterCredential -AuthHeader $AuthHeader -SubscriptionId $SubscriptionId -AVSResourceGroupName $AVSResourceGroupName -AVSPrivateCloudName $AVSPrivateCloudName
$AVSvCenterEndpoint = Get-AVSvCenterEndpoint  -AuthHeader $AuthHeader -SubscriptionId $SubscriptionId -AVSResourceGroupName $AVSResourceGroupName -AVSPrivateCloudName $AVSPrivateCloudName
$vCenterServer = Connect-AVSvCenter -AVSvCenterEndpoint $AVSvCenterEndpoint -AVSvCenterUsername $AVSvCenterCredential.vCenterUsername -AVSvCenterPassword $(ConvertTo-SecureString $AVSvCenterCredential.vCenterPassword -AsPlainText -Force)

Write-Host "Start to build cluster..."
Refresh-PCBSCluster -AvsPrivateCloudName $AVSPrivateCloudName -AvsResourceGroupName $AVSResourceGroupName -vCenterServer $vCenterServer -CBSArrays $CBSArrays
Write-Host "Build cluster is done!"
Disconnect-VIServer -Server $vCenterServer -Confirm:$false -ErrorAction SilentlyContinue