MigrationProfile/AzureMigrationProfile.psm1

Import-Module -Name @(Join-Path  $PSScriptRoot .. | Join-Path -ChildPath Util | Join-Path -ChildPath Util)
function New-RMAzureMigrationProfile {
    param(
        [Parameter(Mandatory)]
        [System.Object] $CloudAccount,

        [Parameter(Mandatory)]
        [System.Object] $CloudAttributes,

        [Parameter(Mandatory)]
        [System.Object] $Entitlement,

        [Parameter(Mandatory)]
        [System.Object] $Source,

        [Parameter(Mandatory)]
        [System.Object] $ScheduledAt,

        [Parameter(Mandatory)]
        [string] $TargetVMName,

        [Parameter(Mandatory)]
        [string] $TransferMethod,

        [Parameter(Mandatory)]
        [string] $ResourceGroup,

        [string] $ResourceGroupRegion,

        [Parameter(Mandatory)]
        [System.Object] $VMSize,

        [System.Object] $DiskTypeMappings,

        [string[]] $VolumeType,

        [Parameter(Mandatory)]
        [System.Object[]] $MountPoints,

        [Parameter(Mandatory)]
        [hashtable] $ResizeMountPoints,

        [Parameter(Mandatory)]
        [string] $VirtualNetwork,

        [Parameter(Mandatory)]
        [string] $DestinationNetworkName,

        [Parameter(Mandatory)]
        [bool] $AssignPublicIP,

        [string] $StaticPrivateIP,

        [string] $SecurityGroup,

        [Parameter(Mandatory)]
        [bool] $DisableTargetDNSRegistration,

        [hashtable] $InstanceTags,

        [string] $AvailabilityZone,

        [string] $AvailabilitySet,

        [System.Object] $FaultDomainCount,

        [System.Object] $UpdateDomainCount,

        [Parameter(Mandatory)]
        [bool] $EnableBootDiagnostics,

        [string] $StorageAccount,

        [string] $EncryptionSet,

        [Parameter(Mandatory)]
        [bool] $EnableHybridUseBenefit,

        [Parameter(Mandatory)]
        [bool] $InPlaceUpgrade,

        [System.Object] $UpgradeOSVersion,

        [Parameter(Mandatory)]
        [bool] $ShutdownSource,

        [Parameter(Mandatory)]
        [bool] $ShutdownTarget,

        [Parameter(Mandatory)]
        [bool] $RemoveRMSAgentFromSource,

        [Parameter(Mandatory)]
        [bool] $RemoveRMSAgentFromTarget,

        [hashtable] $MigrationInstructions,

        [bool] $IgnoreValidationErrors
    )

    if ("" -ne $ResourceGroupRegion) {
        $ReturnedValue = Get-ResourceGroupRegionByUserInput -ResourceGroupRegion $ResourceGroupRegion -CloudAccount $CloudAccount
        if ($null -eq $ReturnedValue) {
            throw "Region '$ResourceGroupRegion' does not exist."
        } else {
            $ResourceGroupRegion = $ReturnedValue
        }
    }

    $license_type = $null
    if ($EnableHybridUseBenefit) {
        $license_type = "Windows_Server"
    }

    $EncryptionSetId = $null
    if ("" -ne $EncryptionSet) {
        $EncryptionSetId = Get-DiskEncryptionSetIdByName -EncryptionSetName $EncryptionSet -CachedCloudAttributes $CloudAttributes
    }


    if ([string]::IsNullOrEmpty($VolumeType)) {
        # For the interactive case, take volume type of the first disk - this is what the UI is doing.
        $VolumeTypeString = $DiskTypeMappings[0]["type"]
    } else {
        $DiskTypeMappings = @()
        $ReturnedValue = Get-DiskTypeMappingBySource -Source $Source -VolumeType $VolumeType -IsInteractive $false
        if ($ReturnedValue -is [hashtable]) {
            # The method "Get-DiskTypeMappingBySource" returns an array of hashtables, but it
            # has been observed that if the source contains a single disk, the returned value
            # is of type HashTable; hence we are checking the type and re-adding it to an array
            $DiskTypeMappings += $ReturnedValue
        } else {
            $DiskTypeMappings = $ReturnedValue
        }
        $VolumeTypeString = $DiskTypeMappings[0]["type"]
    }

    $VirtualNetworkResourceGroup = $null
    $VirtualNetworkResourceGroup = Get-VirtualNetworkResourceGroup -CachedCloudAttributes $CloudAttributes -VirtualNetworkName $VirtualNetwork
    if ($null -eq $VirtualNetworkResourceGroup) {
        throw "Virtual network '$VirtualNetwork' does not exists, cannot start migration"
    }

    $SecurityGroupNode = $null
    if ("" -ne $SecurityGroup -and $null -ne $SecurityGroup) {
        $SecurityGroupNode = Get-SecurityGroup -SecurityGroupName $SecurityGroup -CachedCloudAttributes $CloudAttributes
        if ($null -eq $SecurityGroupNode) {
            throw "Security group '$SecurityGroup' does not exists, cannot start migration"
        }
    }

    if (!$VMSize.vmsizeSupported) {
        $Name = $VMSize.name
        throw "RiverMeadow does not support VM size '$Name', please use a different VM size and try again."
    }

    if ("" -ne $AvailabilityZone -and $VMSize.availability_zones -notcontains $AvailabilityZone) {
        $Name = $VMSize.name
        throw "The VM size '$Name' does not support the availability zone $AvailabilityZone"
    }

    $AvailabilitySetHash = @{}
    if ($null -eq $FaultDomainCount -or $null -eq $UpdateDomainCount) {
        $AvailabilitySetHash.Add("name", $AvailabilitySet)
    } else {
        $AvailabilitySetHash.Add("name", $AvailabilitySet)
        $AvailabilitySetHash.Add("fault_domain_count", $FaultDomainCount)
        $AvailabilitySetHash.Add("update_domain_count", $UpdateDomainCount)
    }
    
    if ([string]::IsNullOrEmpty($ScheduledAt)) {
        # To run "now", FE requires that the $ScheduledAt to be null
        $ScheduledAt = $null
    }

    $CurrentTime = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
    $MigrationProfile = @{
        "name"= "powershell-" + $Source.host + "-" + $CurrentTime
        "tags"= @()
        "entitlement"=$Entitlement.id
        "cloud_account"=$CloudAccount.id
        "is_data_only"= $false
        "is_vdi"= $false
        "appliance_id"= $CloudAccount.appliance.id
        "schedule"= $ScheduledAt
        "transfer_mode"= "run-once"
        "sources"= @(
            @{
                "source"= $Source.id
                "transfer_type"= $TransferMethod
                "target_config"= @{
                    "vm_details"= @{
                        "vm_name"= $TargetVMName
                        "vapp_name"= $TargetVMName
                        "tags"= $InstanceTags
                        "resource_group"= $ResourceGroup
                        "flavor"= @{
                            "flavor_type"= $VMSize.name
                            "volume_type"= $VolumeTypeString
                        }
                        "enable_accelerated_networking"= $VMSize.enable_accelerated_networking
                        "disk_type_mappings"= $DiskTypeMappings
                        "license_type"= $license_type
                        "availability_set"= $AvailabilitySetHash
                        "availability_zone_id"= $AvailabilityZone
                        "enable_boot_diagnostics"= $EnableBootDiagnostics
                        "boot_diagnostics_storage_account"= $StorageAccount
                        "disk_encryption_set_id"= $EncryptionSetId
                    }
                    "properties"= @{
                        "network"= @{
                            "interfaces"= @{
                                "eth0"= @{
                                    "ip_type"= "dhcp"
                                    "type"= "Ethernet"
                                    "network_name"= $DestinationNetworkName
                                    "vnet"= $VirtualNetwork
                                    "assign_public_ip"= $AssignPublicIP
                                    "ip_addr"= $StaticPrivateIP
                                    "resource_group"= $VirtualNetworkResourceGroup
                                    "security_group"= $SecurityGroupNode
                                }
                            }
                            "automatic_dns_registration"= !$DisableTargetDNSRegistration
                        }
                        "selected_mounts"= $MountPoints
                        "name"= $TargetVMName
                        "mounts_new_size"= $ResizeMountPoints
                    }
                    "options"= @{
                        "region"= $CloudAttributes.properties.subscriptions[0].regions[0].name
                        "resource_group_region"= $ResourceGroupRegion
                        "subscription_id"= $CloudAttributes.properties.subscriptions[0].id
                        "network"= @{
                            "name"= $VirtualNetwork
                            "resource_group"= $VirtualNetworkResourceGroup
                        }
                    }
                }
                "os_type"= $Source.os_type
                "name"= $Source.hostname
                "collection_type"= $null
                "shutdown_source"= $ShutdownSource
                "shutdown_target"= $ShutdownTarget
                "remove_source_agent" = $RemoveRMSAgentFromSource
                "remove_target_agent"= $RemoveRMSAgentFromTarget
                "in_place_upgrade"= $InPlaceUpgrade
                "upgrade_os_version"= $UpgradeOSVersion
                "migration_instructions"= $MigrationInstructions
                "data_transfer_port"= $null
                "ignore_validation_errors"= $IgnoreValidationErrors
                "preflight_warning"= $false
           }
        )
    }

    # Giving max depth otherwise the cmdlet 'ConvertTo-Json' will truncate the JSON
    $MigrationProfileJson = $MigrationProfile |ConvertTo-Json -Depth 100

    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" -ValueOnly
    $Uri = Get-Variable -Name "RMContext-ReactorURI" -ValueOnly

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.token
    }

    $Params = @{
        Method = "Post"
        Uri = $Uri + "/migrationprofiles"
        Body = $MigrationProfileJson
        ContentType = "application/json"
        Headers = $Headers
    }

    Invoke-RMRestMethod -Params $Params
}

function Get-VolumeType {
    param(
        [string] $VolumeType
    )
    $VolumeTypes = @{
        "Standard_SSD" = "StandardSSD_LRS"
        "Standard_HDD" = "Standard_LRS"
        "Premium_SSD" = "Premium_LRS"
    }

    return $VolumeTypes[$VolumeType]
}

function Get-DiskEncryptionSetIdByName {
    param(
        [string] $EncryptionSetName,
        [System.Object] $CachedCloudAttributes
    )
    $DiskEncryptionSets = $CachedCloudAttributes.properties.subscriptions[0].regions[0].disk_encryption_sets
    foreach ($DiskEncryptionSet in $DiskEncryptionSets) {
        if ($DiskEncryptionSet.name -ieq $EncryptionSetName) {
            return $DiskEncryptionSet.id
        }
    }

    return $null
}

function Get-DiskTypeMappingBySource {
    param(
        [System.Object] $Source,
        [System.Object] $VMSize,
        [string[]] $VolumeType,
        [bool] $IsInteractive
    )

    $DiskTypeMappings = @()
    $SortedDisks =  $Source.attributes.storage.disks.psobject.Properties.Value | Sort-Object -Property device
    $Index = 0
    foreach ($Disk in  $SortedDisks) {
        if ($IsInteractive) {
            $Size = $Disk.size_kb/(1024*1024)
            $Size = [math]::round($Size, 2)
            $Type = ""
            if ($VMSize.premium_disk_support) {
                $Type = Read-RMString -UserMessage "Enter the volume type for disk with size $Size GiB" `
                    -Options "Standard_SSD", "Standard_HDD", "Premium_SSD" -ParameterName "Volume type" `
                    -DefaultValue "Standard_SSD" -IsRequired $false
            } else {
                $Type = Read-RMString -UserMessage "Enter the volume type for disk with size $Size GiB" `
                -Options "Standard_SSD", "Standard_HDD" -ParameterName "Volume type" `
                -DefaultValue "Standard_SSD" -IsRequired $false
            }
            $Type = Get-VolumeType -VolumeType $Type
        } else {
            if ($VolumeType.Count -gt 1) {
                $Type =  Get-VolumeType -VolumeType $VolumeType[$Index] 
                $Index++
            } else {
                $Type = Get-VolumeType -VolumeType $VolumeType[0]
            }
            
        }
        $DiskMapping = @{
            "source_disk_identifier" = $Disk.device
            "type" = $Type
        }
        $DiskTypeMappings += $DiskMapping
    }
    return $DiskTypeMappings
}

function Get-VirtualNetworkResourceGroup {
    param(
        [System.Object] $CachedCloudAttributes,
        [string] $VirtualNetworkName
    )

    foreach($Network in $CachedCloudAttributes.properties.subscriptions[0].regions[0].networks) {
        if ($Network.name -ieq $VirtualNetworkName) {
            return $Network.resource_group
        }
    }

    return $null
}

function Get-SecurityGroup {
    param(
        [string] $SecurityGroupName,
        [System.Object] $CachedCloudAttributes
    )
    if ("" -eq $SecurityGroup) {
        return $null
    }

    foreach($SecurityGroup in $CachedCloudAttributes.properties.subscriptions[0].regions[0].security_groups) {
        if ($SecurityGroup.name -eq $SecurityGroupName) {
            $Result = @{
                "name" = $SecurityGroupName
                "resource_group" = $SecurityGroup.resource_group
            }
            return $Result
        }
    }

    return $null
}

function Get-VMSizeByName {
    param(
        [string] $VMSizeName,
        [System.Object] $CloudAttributes
    )

    foreach($VMSize in $CloudAttributes.properties.subscriptions[0].regions[0].vm_sizes) {
        if ($VMSize.name -ieq $VMSizeName) {
            return $VMSize
        }
    }

    return $null
}

function Get-ResourceGroupRegionByUserInput {
    param(
        [string] $ResourceGroupRegion,
        [System.Object] $CloudAccount
    )
    $CloudAttributesSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount
    foreach ($Subscription in $CloudAttributesSummary.properties.subscriptions) {
        if (-not($Subscription.id -eq $CloudAccount.appliance.cloud_properties.subscription_id)) {
            continue
        }
        foreach ($Region in $Subscription.regions) {
            if ($Region.name -eq $ResourceGroupRegion -or $Region.label -eq $ResourceGroupRegion) {
                return $Region.name
            }
        }
    }
    return $null
} 

Export-ModuleMember -Function New-RMAzureMigrationProfile, Get-VMSizeByName, Get-DiskTypeMappingBySource