Private/Scriptblocks.ps1

$installD365Module = {
    try {
        Stop-Transcript | out-null
    }
    catch {
        $error.clear()   
    }
    New-Item -ItemType Directory -Force -Path C:\Install\Log -ErrorAction SilentlyContinue | Out-Null
    Start-Transcript -Path "C:\Install\Log\InstallD365Module.$(get-date -format yyyyMMddhhmmss).log"
    if (Get-Module -Name SetupD365Environment -ListAvailable) {
        Write-Host "Updating module SetupD365Environment..."
        Update-Module -Name SetupD365Environment -Force
    }
    else {
        Write-Host "Installing module SetupD365Environment..."
        Install-Module -Name SetupD365Environment -Force
    }
    Stop-Transcript
}

$initializeVm = {
    $ErrorActionPreference = "SilentlyContinue"
    try {
        Stop-Transcript | out-null
    }
    catch {
        $error.clear()
    }
    New-Item -ItemType Directory -Force -Path C:\Install\Log -ErrorAction SilentlyContinue | Out-Null
    Start-Transcript -Path "C:\Install\Log\InitVM.$(get-date -format yyyyMMddhhmmss).log"
    Import-Module -Name SetupD365Environment
    if (Get-Module -Name Cloud.Ready.Software.NAV -ListAvailable) {
        Write-Host "Updating module Cloud.Ready.Software.NAV..."
        Update-Module -Name Cloud.Ready.Software.NAV -Force
    }
    else {
        Write-Host "Installing module Cloud.Ready.Software.NAV..."
        Install-Module -Name Cloud.Ready.Software.NAV -Force
    }
    Initialize-CustomAzVm
    Stop-Transcript
}

$downloadBCDVD = {
    Param(
        [Parameter(Mandatory = $false, Position = 3)]
        [string]
        $Version,
        [Parameter(Mandatory = $false, Position = 4)]
        [string]
        $CumulativeUpdate,
        [Parameter(Mandatory = $false, Position = 5)]
        [string]
        $Language
    )
    try {
        Stop-Transcript | out-null
    }
    catch {
        $error.clear()
    }
    Start-Transcript -Path "C:\Install\Log\DownloadBC.$(get-date -format yyyyMMddhhmmss).log"
    Import-Module SetupD365Environment
    Write-Host "Version: $Version CU: $CumulativeUpdate Lang: $Language..."
    Receive-BusinessCentralDVD -Version $Version -CumulativeUpdate $CumulativeUpdate -Language $Language
    Stop-Transcript
}

$installBC = {
    Param(
        [Parameter(Mandatory = $false, Position = 1)]
        [string]
        $DownloadDirectory,
        [Parameter(Mandatory = $false, Position = 2)]
        [string]
        $ConfigurationFile,
        [Parameter(Mandatory = $false, Position = 3)]
        [string]
        $LicenseFilename,
        [Parameter(Mandatory = $false, Position = 4)]
        [ValidateSet('13', '14', '15')]
        [string]
        $Version,
        [ValidateSet('App', 'Web')]
        [Parameter(Mandatory = $false, Position = 5)]
        [string]
        $InstallationType = "App"
    )
    try {
        Stop-Transcript | out-null
    }
    catch {
        $error.clear()
    }    
    Start-Transcript -Path "C:\Install\Log\InstallBC.$(get-date -format yyyyMMddhhmmss).log"
    # TODO: Remove later >>
    Update-Module -Name SetupD365Environment -Force
    # TODO <<
    Import-Module SetupD365Environment
    Write-Host "Installing Business Central"    
    $InstallArgs = @{        
    }
    if (-not([string]::IsNullOrEmpty($DownloadDirectory))) {
        $InstallArgs.Add('DownloadDirectory', $DownloadDirectory)
    }
    if (-not([string]::IsNullOrEmpty($ConfigurationFile))) {
        $InstallArgs.Add('ConfigurationFile', $ConfigurationFile)
    }
    if (-not([string]::IsNullOrEmpty($LicenseFilename))) {
        $InstallArgs.Add('LicenseFilename', $LicenseFilename)
    }
    if (-not([string]::IsNullOrEmpty($Version))) {
        $InstallArgs.Add('Version', $Version)
    }
    if (-not([string]::IsNullOrEmpty($InstallationType))) {
        $InstallArgs.Add('InstallationType', $InstallationType)
    }
    Install-BusinessCentral @InstallArgs
    
    # Set default Service to disabled
    Get-Service | Where-Object { $_.Name -like 'MicrosoftDynamicsNavServer*' } | Set-Service -StartupType Disabled
    Get-Service | Where-Object { $_.Name -like 'MicrosoftDynamicsNavServer*' } | Stop-Service
    Stop-Transcript
}

$generalizeVM = {
    try {
        Stop-Transcript | out-null
    }
    catch {
        $error.clear()
    }    
    Start-Transcript -Path "C:\Install\Log\GeneralizeVM.$(get-date -format yyyyMMddhhmmss).log"
    Write-Host "Generalizing VM. "
    Write-Host "About to call 'Sysprep.exe /generalize /oobe /shutdown /quiet'"
    Stop-Transcript
    $sysprep = 'C:\Windows\System32\Sysprep\Sysprep.exe'
    $arg = '/generalize /oobe /shutdown'
    Start-Process -FilePath $sysprep -ArgumentList $arg    
}

$writeProperties = {
    param(
        [Parameter(Mandatory = $false, Position = 1)]
        [string]
        $VMName,
        [Parameter(Mandatory = $false, Position = 2)]
        [string]
        $ScaleSetName,
        [Parameter(Mandatory = $true, Position = 3)]
        [string]
        $ResourceGroupName,
        [Parameter(Mandatory = $true, Position = 4)]
        [string]
        $StorageAccountName,
        [Parameter(Mandatory = $true, Position = 5)]
        [string]
        $KeyVaultName,
        [Parameter(Mandatory = $true, Position = 6)]
        [string]
        $StorageTableNameSetup,
        [Parameter(Mandatory = $true, Position = 7)]
        [string]
        $StorageTableNameEnvironments,
        [Parameter(Mandatory = $true, Position = 8)]
        [string]
        $StorageTableNameEnvironmentDefaults
    )
    $targetFolder = 'C:\Install\AutoUpdate'
    New-Item -ItemType Directory -Path $targetFolder -ErrorAction SilentlyContinue | Out-Null
    $fullscriptpath = Join-Path $targetFolder 'Properties.ps1'
    
    $content = "
    `$VMName = '$VMName'
    `$ScaleSetName = '$ScaleSetName'
    `$ResourceGroupName = '$ResourceGroupName'
    `$StorageAccountName = '$StorageAccountName'
    `$KeyVaultName = '$KeyVaultName'
    `$StorageTableNameSetup = '$StorageTableNameSetup'
    `$StorageTableNameEnvironments = '$StorageTableNameEnvironments'
    `$StorageTableNameEnvironmentDefaults = '$StorageTableNameEnvironmentDefaults'
    "
    
    Set-Content -Path $fullscriptpath -Value $content
}

$createUpdateScheduledTask = {
    $targetFolder = 'C:\Install\AutoUpdate'
    New-Item -ItemType Directory -Path $targetFolder -ErrorAction SilentlyContinue | Out-Null
    
    $fullscriptpath = Join-Path $targetFolder 'AutoUpdate.ps1'
    
    $scriptblock = {
        try {
            Stop-Transcript | out-null
        }
        catch {
            $error.clear()
        }
        Start-Transcript -Path "C:\Install\Log\AutoUpdate.$(get-date -format yyyyMMddhhmmss).log"        
        if (Get-Module -Name SetupD365Environment -ListAvailable) {
            Update-Module -Name SetupD365Environment -Force
        }
        else {
            Install-Module -Name SetupD365Environment -Force
        }
        Import-Module -Name SetupD365Environment
        Test-CustomNetworkAvailable
        #if ((Set-CustomNetworkSettings) -eq $true) {
        # Write-Host "Updated network settings. Restarting machine..."
        # Restart-Computer -Force
        # return
        #}
        . ("C:\Install\AutoUpdate\Properties.ps1")
        $Instance = $false
        if (-not([string]::IsNullOrEmpty($VMName))) {
            $ObjectName = $VMName
        }
        else {
            $ObjectName = $ScaleSetName
            $Instance = $true
        }
        Start-CustomVMUpdate -ObjectName $ObjectName -IsScaleSet:$Instance -ResourceGroupName $ResourceGroupName -StorageAccountName $StorageAccountName -KeyVaultName $KeyVaultName -StorageTableNameSetup $StorageTableNameSetup -StorageTableNameEnvironments $StorageTableNameEnvironments -StorageTableNameEnvironmentDefaults $StorageTableNameEnvironmentDefaults
        Stop-Transcript
    }
    
    Set-Content -Path $fullscriptpath -Value $scriptblock
    
    $action = New-ScheduledTaskAction -Execute 'Powershell.exe' `
        -Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Unrestricted -File `"$fullscriptpath`""
    $trigger = New-ScheduledTaskTrigger -AtStartup
    $taskPrinicpal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType  ServiceAccount -RunLevel Highest
    Register-ScheduledTask -Action $action -Trigger $trigger -Principal $taskPrinicpal -TaskName "CustomAutoUpdate" -Description "Update Azure Machine" | Out-Null   
}

$invokeAutoUpdate = {    
    . ("C:\Install\AutoUpdate\AutoUpdate.ps1")
}

$setNetConnectionProfile = {    
    $restartNecessary = $false
    foreach ($netConProfile in Get-NetConnectionProfile) {
        if ($netConProfile.NetworkCategory -ne 'Private') {
            Set-NetConnectionProfile -Name $netConProfile.Name -NetworkCategory Private
            $restartNecessary = $true
        }        
    }
    foreach ($profile in Get-NetConnectionProfile | Where-Object { $_.InterfaceAlias -like 'Ethernet*' }) {
        Set-DnsClientServerAddress -InterfaceIndex $profile.InterfaceIndex -ServerAddresses ("10.0.0.4") # TODO: Change to DC IP
    }
    if ($restartNecessary) {
        Restart-Computer -Force
    }    
}

$enableRemoting = {    
    Enable-PSRemoting -Force
    Set-NetFirewallRule -Name "WINRM-HTTP-In-TCP-PUBLIC" -RemoteAddress Any
    Set-NetFirewallRule -Name "WINRM-HTTP-In-TCP-PUBLIC" -LocalAddress Any
}

$waitForNetwork = {
    $done = $false
    $success = $false
    $noOfTries = 0
    $StartDate = (Get-Date)    
    while (-not($done)) {
        try {
            $noOfTries
            if (Test-NetConnection) {
                $done = $true
                $success = $true
            }
            else {
                Start-Sleep -Seconds 2
            }
        }
        catch {
            # Do nothing
        }
        $noOfTries++
        if ($noOfTries -ge 30) {
            # 50 tries max
            $done = $true
        }
        $duration = New-TimeSpan -Start $StartDate -End (Get-Date)
        if ($duration.Seconds -ge 300) {
            # Wait 5 Minutes top
            $done = $true
        }
    }
    if (-not($success)) {
        throw "Checked Test-NetConnection $noOfTries times, without success."
    }
}

$saveDiagInformation = {
    $filename = "C:\Install\Log\DiagnoseInformation.$(get-date -format yyyyMMddhhmmss).log"
    "Output from: Get-NetIPConfiguration -All" | Out-File -FilePath $filename -Append
    "========================================" | Out-File -FilePath $filename -Append
    Get-NetIPConfiguration -All | Out-File -FilePath $filename -Append
    "========================================" | Out-File -FilePath $filename -Append
    "Output from: Get-NetConnectionProfile" | Out-File -FilePath $filename -Append
    "========================================" | Out-File -FilePath $filename -Append
    Get-NetConnectionProfile | Out-File -FilePath $filename -Append
    "========================================" | Out-File -FilePath $filename -Append
    "Output from: Get-Service | Format-Table -AutoSize" | Out-File -FilePath $filename -Append
    "========================================" | Out-File -FilePath $filename -Append
    Get-Service | Format-Table -AutoSize | Out-File -FilePath $filename -Append
    "========================================" | Out-File -FilePath $filename -Append
}

$configureSqlServer = {
    param(        
        [Parameter(Mandatory = $true)]
        [string]
        $SqlUser,
        [Parameter(Mandatory = $true)]
        [string]
        $SqlPass
    )
    try {
        Stop-Transcript | out-null
    }
    catch {
        $error.clear()
    }
    New-Item -ItemType Directory -Force -Path C:\Install\Log -ErrorAction SilentlyContinue | Out-Null
    Start-Transcript -Path "C:\Install\Log\SqlConfig.$(get-date -format yyyyMMddhhmmss).log"
    $scriptBlock = {
        param(        
            [Parameter(Mandatory = $true)]
            [string]
            $SqlUser,
            [Parameter(Mandatory = $true)]
            [string]
            $SqlPass
        )
        $assemblylist =   
        "Microsoft.SqlServer.Management.Common",  
        "Microsoft.SqlServer.Smo",  
        "Microsoft.SqlServer.Dmf ",  
        "Microsoft.SqlServer.Instapi ",  
        "Microsoft.SqlServer.SqlWmiManagement ",  
        "Microsoft.SqlServer.ConnectionInfo ",  
        "Microsoft.SqlServer.SmoExtended ",  
        "Microsoft.SqlServer.SqlTDiagM ",  
        "Microsoft.SqlServer.SString ",  
        "Microsoft.SqlServer.Management.RegisteredServers ",  
        "Microsoft.SqlServer.Management.Sdk.Sfc ",  
        "Microsoft.SqlServer.SqlEnum ",  
        "Microsoft.SqlServer.RegSvrEnum ",  
        "Microsoft.SqlServer.WmiEnum ",  
        "Microsoft.SqlServer.ServiceBrokerEnum ",  
        "Microsoft.SqlServer.ConnectionInfoExtended ",  
        "Microsoft.SqlServer.Management.Collector ",  
        "Microsoft.SqlServer.Management.CollectorEnum",  
        "Microsoft.SqlServer.Management.Dac",  
        "Microsoft.SqlServer.Management.DacEnum",  
        "Microsoft.SqlServer.Management.Utility"  
  
        foreach ($asm in $assemblylist) {  
            $asm = [Reflection.Assembly]::LoadWithPartialName($asm)  
        } 
        $s = new-object ('Microsoft.SqlServer.Management.Smo.Server') $env:computername
        $s.Name
        $s.LoginMode
        $s.Settings.LoginMode = [Microsoft.SqlServer.Management.SMO.ServerLoginMode]::Mixed
        $s.Alter()
        Get-Service -Name MSSQLSERVER | Restart-Service -Force
        $SQLLogin = [Microsoft.SqlServer.Management.Smo.Login]::New($s, $SqlUser)
        $SQLLogin.LoginType = [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin
        $SQLLogin.PasswordPolicyEnforced = $False
        $SQLLogin.Create((ConvertTo-SecureString $SqlPass -AsPlainText -Force))
        $svrole = $s.Roles | where { $_.Name -eq 'sysadmin' };
        $svrole.AddMember($SqlUser);
    }
    try {        
        $VMCredentials = New-Object System.Management.Automation.PSCredential ("$($env:computername)\$SqlUser", (ConvertTo-SecureString $SqlPass -AsPlainText -Force))
        #Enter-PSSession -ComputerName $env:computername -Credential $VMCredentials
        $session = New-PSSession -ComputerName $env:computername -Credential $VMCredentials
        #Invoke-Command -computername $env:computername -Credential $VMCredentials -ScriptBlock $scriptBlock -ArgumentList $SqlUser, $SqlPass
        #Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $SqlUser, $SqlPass
        Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $SqlUser, $SqlPass -Session $session
        #Exit-PSSession
        $session | Remove-PSSession
    }
    catch {
        # Do nothing
    }
    Stop-Transcript
}

$configureSqlServerDomain = {
    param(        
        [Parameter(Mandatory = $true)]
        [string]
        $SqlUser,
        [Parameter(Mandatory = $true)]
        [string]
        $SqlPass,
        [Parameter(Mandatory = $true)]
        [string]
        $DomainName
    )
    try {
        Stop-Transcript | out-null
    }
    catch {
        $error.clear()
    }
    New-Item -ItemType Directory -Force -Path C:\Install\Log -ErrorAction SilentlyContinue | Out-Null
    Start-Transcript -Path "C:\Install\Log\SqlConfigDomain.$(get-date -format yyyyMMddhhmmss).log"
    $scriptBlock = {
        param(        
            [Parameter(Mandatory = $true)]
            [string]
            $SqlUser,
            [Parameter(Mandatory = $true)]
            [string]
            $SqlPass,
            [Parameter(Mandatory = $true)]
            [string]
            $DomainName
        )
        $assemblylist =   
        "Microsoft.SqlServer.Management.Common",  
        "Microsoft.SqlServer.Smo",  
        "Microsoft.SqlServer.Dmf ",  
        "Microsoft.SqlServer.Instapi ",  
        "Microsoft.SqlServer.SqlWmiManagement ",  
        "Microsoft.SqlServer.ConnectionInfo ",  
        "Microsoft.SqlServer.SmoExtended ",  
        "Microsoft.SqlServer.SqlTDiagM ",  
        "Microsoft.SqlServer.SString ",  
        "Microsoft.SqlServer.Management.RegisteredServers ",  
        "Microsoft.SqlServer.Management.Sdk.Sfc ",  
        "Microsoft.SqlServer.SqlEnum ",  
        "Microsoft.SqlServer.RegSvrEnum ",  
        "Microsoft.SqlServer.WmiEnum ",  
        "Microsoft.SqlServer.ServiceBrokerEnum ",  
        "Microsoft.SqlServer.ConnectionInfoExtended ",  
        "Microsoft.SqlServer.Management.Collector ",  
        "Microsoft.SqlServer.Management.CollectorEnum",  
        "Microsoft.SqlServer.Management.Dac",  
        "Microsoft.SqlServer.Management.DacEnum",  
        "Microsoft.SqlServer.Management.Utility"  
  
        foreach ($asm in $assemblylist) {  
            $asm = [Reflection.Assembly]::LoadWithPartialName($asm)  
        } 
        $s = new-object ('Microsoft.SqlServer.Management.Smo.Server') $env:computername        
        Get-Service -Name MSSQLSERVER | Restart-Service -Force
        $SQLLogin = [Microsoft.SqlServer.Management.Smo.Login]::New($s, "$DomainName\$SqlUser")
        $SQLLogin.LoginType = [Microsoft.SqlServer.Management.Smo.LoginType]::WindowsUser
        $SQLLogin.PasswordPolicyEnforced = $False
        $SQLLogin.Create((ConvertTo-SecureString $SqlPass -AsPlainText -Force))
        $svrole = $s.Roles | where { $_.Name -eq 'sysadmin' };
        $svrole.AddMember("$DomainName\$SqlUser");
    }
    try {        
        $VMCredentials = New-Object System.Management.Automation.PSCredential ("$($env:computername)\$SqlUser", (ConvertTo-SecureString $SqlPass -AsPlainText -Force))
        #Enter-PSSession -ComputerName $env:computername -Credential $VMCredentials
        $session = New-PSSession -ComputerName $env:computername -Credential $VMCredentials
        #Invoke-Command -computername $env:computername -Credential $VMCredentials -ScriptBlock $scriptBlock -ArgumentList $SqlUser, $SqlPass
        #Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $SqlUser, $SqlPass
        Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $SqlUser, $SqlPass, $DomainName -Session $session
        #Exit-PSSession
        $session | Remove-PSSession
    }
    catch {
        # Do nothing
    }
    Stop-Transcript
}