<#

.SYNOPSIS
    SAP on Azure NVMe conversion

.DESCRIPTION
    The script converts a VM from SCSI to NVMe controller

.LINK
    https://github.com/Azure/SAP-on-Azure-Scripts-and-Utilities

#>

<#
    Copyright (c) Microsoft Corporation.
    Licensed under the MIT license.
#>

#Requires -Version 7.1

[CmdletBinding()]
param (
    # Subscription ID
    [Parameter(Mandatory=$true)][string]$subscription_id,
    # Resource Group
    [Parameter(Mandatory=$true)][string]$resource_group_name,
    # VM Name
    [Parameter(Mandatory=$true)][string]$vm_name,
    # Disk Controller Type
    [ValidateSet("NVMe", "SCSI")][string]$disk_controller_change_to="NVMe",
    # New VM Size
    [Parameter(Mandatory=$true)][string]$vm_size_change_to,
    # Start VM after update
    [bool]$start_vm_after_update = $false,
    # Write Log File
    [bool]$write_logfile = $false,
    # Ignore VM availability check
    [switch]$ignore_vmsku_check
)

# RunLog function for more detailed data during execution
function WriteRunLog {
    [CmdletBinding()]
    param (
        [string]$message,
        [string]$category="INFO"
    )

    switch ($category) {
        "INFO"      {   $_prestring = "INFO     - "
                        $_color = "Green" }
        "WARNING"   {   $_prestring = "WARNING  - "
                        $_color = "Yellow" }
        "ERROR"     {   $_prestring = "ERROR    - "
                        $_color = "Red" }
    }
    $_runlog_row = "" | Select-Object "Log"
    $_runlog_row.Log = [string]$_prestring + [string]$message
    $script:_runlog += $_runlog_row
    if (-not $RunLocally) {
        Write-Host ($_prestring + $message) -ForegroundColor $_color
    }
}


################################################################################################
#
# script start
#
################################################################################################


# create variable for log
$script:_runlog = @()

$breakingchangewarning = Get-AzConfig -DisplayBreakingChangeWarning
if ($breakingchangewarning.Value -eq $true) {
    Update-AzConfig -DisplayBreakingChangeWarning $false
}

# check if connected to Azure
$_SubscriptionInfo = Get-AzSubscription -SubscriptionId $subscription_id

# if $_SubscritpionInfo then it got subscriptions
if ($_SubscriptionInfo)
{

    $_ContextInfo = Get-AzContext

    if ($_ContextInfo.Subscription -eq $subscription_id) {
        # connected to correct context
        WriteRunLog -category "INFO" -message "Already connected to correct Azure context"
    }
    else {
        # setting context to correct subscription
        Set-AzContext -Subscription $subscription_id
    }
}
else {
    WriteRunLog -category "ERROR" -message "Please connect to Azure using the Connect-AzAccount command, if you are connected use the Select-AzSubscription command to set the correct context"
    exit
}

# Getting OS disk name
$os_disk_name = (Get-AzVM -ResourceGroupName $resource_group_name -Name $vm_name).StorageProfile.OsDisk.Name

if ($os_disk_name) {
    # found OS Disk
    WriteRunLog -category "INFO" -message "OS Disk found"
}
else 
{
    WriteRunLog -category "ERROR" -message "Please check the OS Disk"
}

# gettting Access Token for Web ARM request
$access_token = (Get-AzAccessToken).Token

if ($access_token) {
    # Access token valid
    WriteRunLog -category "INFO" -message "Access token generated"
}
else {
    WriteRunLog -category "ERROR" -message "Problems creating access token"
}

# generating URI for the OS disk update
$uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/disks/{2}?api-version=2023-04-02' -f $subscription_id, $resource_group_name, $os_disk_name

# auth header for web request
$auth_header = @{
  'Content-Type'  = 'application/json'
  'Authorization' = 'Bearer ' + $access_token
}

# body for SCSI/NVMe enabled OS Disk
$body_nvmescsi = @'
{
    "properties": {
        "supportedCapabilities": {
            "diskControllerTypes":"SCSI, NVMe"
        }
    }
}
'@

# body for SCSI enabled OS Disk
$body_scsi = @'
{
    "properties": {
        "supportedCapabilities": {
            "diskControllerTypes":"SCSI"
        }
    }
}
'@

$_continue = 0

WriteRunLog -category "INFO" -message "Getting VM info"
$_vm = Get-AzVM -ResourceGroupName $resource_group_name -Name $vm_name
$_vminfo = Get-AzVM -ResourceGroupName $resource_group_name -Name $vm_name -Status

# checking VM capabilities
WriteRunLog -category "INFO" -message ("Getting all VM SKUs available in Region " + $_vm.Location)
WriteRunLog -category "INFO" -message ("This will take about a minute ...")
if (-not $ignore_vmsku_check) {
    $_VMSKUs = Get-AzComputeResourceSku | Where-Object { $_.Locations -contains $_vm.Location -and $_.ResourceType.Contains("virtualMachines") }
    $_VMSKU = $_VMSKUs | Where-Object { $_.Name -eq $vm_size_change_to }
    if ($_VMSKU) {
        WriteRunLog -category "INFO" -message "Found VM SKU - Checking for Capabilities"
        $_supported_controller = ($_VMSKU.Capabilities | Where-Object { $_.Name -eq "DiskControllerTypes" }).Value

        if ([string]::IsNullOrEmpty($_supported_controller)) {
            WriteRunLog -category "ERROR" -message "VM SKU doesn't have supported capabilities"
            $_continue -= 100
        }
        else {
            WriteRunLog -category "INFO" -message "VM SKU has supported capabilities"
            if ($disk_controller_change_to -eq "NVMe") {
                # NVMe destination
                if ($_supported_controller.Contains("NVMe") ) {
                    WriteRunLog -category "INFO" -message "VM supports NVMe"
                    $_continue += 1
                }
                else {
                    WriteRunLog -category "ERROR" -message "VM doesn't support NVMe"
                    $_continue -= 100
                }
            }
            else {
                # SCSI destination
                $_continue += 1 # all VMs support SCSI
            }  
        }
    }
    else {
        WriteRunLog -category "ERROR" -message ("VM SKU doesn't exist, please check your input: " + $vm_size_change_to )
        $_continue -= 100
    }
}
else {
    $_continue = 1
}

WriteRunLog -category "INFO" -message "Checking for TrustedLaunch"
if ($_vm.SecurityProfile.SecurityType -eq "TrustedLaunch") {
    WriteRunLog -category "ERROR" -message "The VM is configured with Trusted Launch, NVMe doesn't support trusted launch."
    $_continue -= 100
}

# checking if the $_continue variable is positive or negative
# for any case where it is negative a pre-requisit hasn't been met
if ($_continue -gt 0) {
    WriteRunLog -category "INFO" -message "Checking if VM is stopped and deallocated"
    if ($_vminfo.Statuses[1].Code -eq "PowerState/deallocated") {
        # VM is already stopped
        WriteRunLog -category "INFO" -message "VM is stopped and deallocated"
    }
    else {
        #Stop and deallocate the VM
        WriteRunLog -category "INFO" -message "Stopping VM"
        Stop-AzVM -ResourceGroupName $resource_group_name -Name $vm_name -Force
    }

    if ($disk_controller_change_to -eq "NVMe") {
        #Add NVMe supported capabilities to the OS disk
        WriteRunLog -category "INFO" -message "Setting OS Disk to SCSI/NVMe"
        $Update_Supported_Capabilities = (Invoke-WebRequest -uri $uri -Method PATCH -body $body_nvmescsi -Headers $auth_header | ConvertFrom-Json)
    }
    else {
        #Add NVMe supported capabilities to the OS disk
        WriteRunLog -category "INFO" -message "Setting OS Disk to SCSI"
        $Update_Supported_Capabilities = (Invoke-WebRequest -uri $uri -Method PATCH -body $body_scsi -Headers $auth_header | ConvertFrom-Json)
    }

    # Get VM configuration
    WriteRunLog -category "INFO" -message "Getting VM config to prepare new config"
    $vm = Get-AzVM -ResourceGroupName $resource_group_name -Name $vm_name

    # Set new VM size
    WriteRunLog -category "INFO" -message "Setting new VM size"
    $vm.HardwareProfile.VmSize = $vm_size_change_to

    # Set new Controller type for VM
    WriteRunLog -category "INFO" -message "Setting disk controller for VM"
    $vm.StorageProfile.DiskControllerType = $disk_controller_change_to

    # Update the VM
    WriteRunLog -category "INFO" -message "Updating the VM configuration"
    Update-AzVM -ResourceGroupName $resource_group_name -VM $vm

    if ($start_vm_after_update) {
        # Start the VM
        WriteRunLog -category "INFO" -message "Waiting for 1 min before starting up"
        Start-Sleep 60
        WriteRunLog -category "INFO" -message "Starting VM"
        Start-AzVM -ResourceGroupName $resource_group_name -Name $vm_name
    }
    else {
        # Do not start VM
        WriteRunLog -category "INFO" -message "Not starting VM"
    }
}
else {
    WriteRunLog -category "ERROR" -message "Pre-requisits check failed"
}

if ($write_logfile)
{
    WriteRunLog -category "INFO" -message "Writing logfile"
    $_filename = ".\" + $vm_name + "_" + (Get-Date -Format "yyyyMMdd-HHmm") + ".txt"
    $script:_runlog | Out-File -FilePath $_filename -Append
}

if ($breakingchangewarning.Value -eq $true) {
    Update-AzConfig -DisplayBreakingChangeWarning $true
}

################################################################################################
#
# script end
#
################################################################################################