Private/Start-CustomVMUpdate.ps1

# Will be called in VM
function Global:Start-CustomVMUpdate {
    [CmdletBinding()]
    <#
    .SYNOPSIS
        ...
        Parameters for Commands
        Command | Parameter 1 | Parameter 2 |
        JoinDomain | none | none |
        CreateInstances | Typefilter (e.g. "TEST") [x] | "RestartService" [ ] |
        UpdateInstanceConfiguration | Typefilter (e.g. "TEST") [x] | "RestartService" [ ] |
        UpdateLicense | URI to Licensefile [x] | "RestartService" [ ] |
        ...
    .DESCRIPTION
        ...
    #>

    param(
        [Parameter(Mandatory = $true, Position = 1)]
        [string]
        $ObjectName,
        [Parameter(Mandatory = $false, Position = 2)]
        [switch]
        $IsScaleSet,
        [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,
        [Parameter(Mandatory = $true, Position = 9)]
        [string]
        $StorageTableNameInfrastructureData
    )
    process {
        Write-Host "Starting auto update..."

        # Uses managed identity to connect to Azure Account
        Connect-FromMachineToAzAccount
        
        Write-Host "Loading pending commands..."
        $storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName
        $storageAccountContext = $storageAccount.Context
        $storageAccountTable = Get-AzStorageTable -Name $StorageTableNameSetup -Context $storageAccountContext.Context
        $cloudTable = $storageAccountTable.CloudTable
        #$rows = Get-AzTableRow -Table $cloudTable | Where-Object { ($_.ExecutedBy -notlike "*$env:computername*") -and (($_.ObjectName -eq 'NULL') -or ($_.ObjectName -eq $ObjectName)) }
        $rows = Get-AzTableRow -Table $cloudTable | Where-Object { ($_.ObjectName -eq 'NULL') -or ($_.ObjectName -eq $ObjectName) }
        foreach ($row in $rows) {
            Write-Host "Handling Command: $($row.Command)"
            switch ($row.Command) {
                # This command is not really used anymore; was in the beginning, but now it's done via a VM Extension
                'JoinDomain' {
                    if ((Get-WmiObject win32_computersystem).partofdomain -eq $false) { 
                        Write-Host "Loading credentials..."
                        # Load Admin Credentials from KeyVault
                        $adminUserName = (Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name 'DomainAdminUsername').SecretValueText
                        $adminUserPass = (Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name 'DomainAdminPassword').SecretValueText
                        $domainName = (Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name 'DomainName').SecretValueText                    
                        $adminUserName = "$domainName\$adminUserName"
                        $domainAdminCredentials = New-Object System.Management.Automation.PSCredential ($adminUserName, (ConvertTo-SecureString -String $adminUserPass -AsPlainText -Force))
                        if ($row.RestartNecessary -eq $true) {
                            # Update Command Table, because machine will be restartet in a moment
                            Set-StorageCommandExecuted -CommandRow $row -ExecutedByName $env:computername
                        }
                        Add-Computer -DomainName $domainName -Credential $domainAdminCredentials -Restart -Force | Out-Null
                    }
                    else {
                        Write-Host "Machine is already part of domain."
                    }
                }
                'CreateInstances' {
                    Write-Host "Importing Cloud.Ready.Software.NAV..."
                    Import-Module Cloud.Ready.Software.NAV -Force
                    Write-Host "Importing NAV Modules..."
                    Import-NAVModules

                    $environments = Get-EnvironmentsFromStorage -StorageAccountContext $storageAccountContext -TableNameEnvironments $StorageTableNameEnvironments -TableNameDefaults $StorageTableNameEnvironmentDefaults -TypeFilter $row.Parameter1 -ConfigType Application
                    foreach ($environment in $environments) {
                        if (-not (Get-NavServerInstance -ServerInstance $environment.ServerInstance)) {
                            Write-Host "Creating instance: $($environment.ServerInstance)"
                            New-NAVServerInstance $environment.ServerInstance `
                                -DatabaseServer $environment.DatabaseServer  `
                                -DatabaseInstance $environment.DatabaseInstance  `
                                -Databasename $environment.Databasename `
                                -ClientServicesPort $environment.ClientServicesPort   `
                                -ManagementServicesPort $environment.ManagementServicesPort `
                                -SOAPServicesPort $environment.SoapServicesPort   `
                                -ODataServicesPort $environment.OdataServicesPort
                            
                            Write-Host "Changing port-configuration..."
                            Set-NAVServerConfiguration -ServerInstance $environment.ServerInstance -KeyName DeveloperServicesPort -KeyValue $environment.DeveloperServicesPort                            
                            Write-Host "Updating authentication-method..."
                            Set-NAVServerConfiguration -ServerInstance $environment.ServerInstance -KeyName ClientServicesCredentialType -KeyValue $environment.Authentication

                            Write-Host "Updating service-account..."
                            $adminUserName = (Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name 'DomainAdminUsername').SecretValueText
                            $adminUserPass = (Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name 'DomainAdminPassword').SecretValueText
                            $domainName = (Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name 'DomainName').SecretValueText          
                            $adminUserName = "$domainName\$adminUserName"

                            # TODO: Switch to different user (create setup?)
                            $credentialsObject = New-Object System.Management.Automation.PSCredential ($adminUserName, (ConvertTo-SecureString $adminUserPass -AsPlainText -Force))
                            Set-NAVServerInstance -ServerInstance $environment.ServerInstance -ServiceAccount User -ServiceAccountCredential $credentialsObject

                            foreach ($key in $environment.Settings.Keys) {
                                if ((-not([string]::IsNullOrEmpty($key))) -and (-not([string]::IsNullOrEmpty($environment.Settings[$key])))) {
                                    Set-NAVServerConfiguration -ServerInstance $environment.ServerInstance -KeyName $key -KeyValue $environment.Settings[$key]
                                }
                            }
                            if ($row.Parameter2 -eq "RestartService") {
                                Write-Host "Restarting service"
                                Restart-NAVServerInstance -ServerInstance $environment.ServerInstance
                            }
                        }                        
                    }
                }
                'UpdateInstanceConfiguration' {
                    Write-Host "Importing Cloud.Ready.Software.NAV..."
                    Import-Module Cloud.Ready.Software.NAV -Force
                    Write-Host "Importing NAV Modules..."
                    Import-NAVModules

                    $environments = Get-EnvironmentsFromStorage -StorageAccountContext $storageAccountContext -TableNameEnvironments $StorageTableNameEnvironments -TableNameDefaults $StorageTableNameEnvironmentDefaults -TypeFilter $row.Parameter1 -ConfigType Application
                    foreach ($environment in $environments) {
                        if (Get-NavServerInstance -ServerInstance $environment.ServerInstance) {
                            $config = Get-NAVServerConfiguration -ServerInstance $environment.ServerInstance

                            # Convert Result to HashTable
                            $currentConf = @{ }
                            $config | ForEach-Object { $currentConf[$_.key] = $_.value }

                            $updated = $false
                            $updated = $updated -or (Set-NAVConfigurationIfDifferent -ServerInstance $environment.ServerInstance -KeyName DeveloperServicesPort -KeyValue $environment.DeveloperServicesPort -CurrentConfiguration $config -Verbose)
                            $updated = $updated -or (Set-NAVConfigurationIfDifferent -ServerInstance $environment.ServerInstance -KeyName ODataServicesPort -KeyValue $environment.OdataServicesPort -CurrentConfiguration $config -Verbose)
                            $updated = $updated -or (Set-NAVConfigurationIfDifferent -ServerInstance $environment.ServerInstance -KeyName SOAPServicesPort -KeyValue $environment.SoapServicesPort -CurrentConfiguration $config -Verbose)
                            $updated = $updated -or (Set-NAVConfigurationIfDifferent -ServerInstance $environment.ServerInstance -KeyName ClientServicesCredentialType -KeyValue $environment.Authentication -CurrentConfiguration $config -Verbose)
                            
                            foreach ($key in $environment.Settings.Keys) {
                                if ((-not([string]::IsNullOrEmpty($key))) -and (-not([string]::IsNullOrEmpty($environment.Settings[$key])))) {
                                    $updated = $updated -or (Set-NAVConfigurationIfDifferent -ServerInstance $environment.ServerInstance -KeyName $key -KeyValue $environment.Settings[$key] -CurrentConfiguration $config -Verbose)
                                }
                            }
                            if ($updated -and ($row.Parameter2 -eq "RestartService")) {
                                Write-Host "Restarting service"
                                Restart-NAVServerInstance -ServerInstance $environment.ServerInstance
                            }
                        }
                    }
                }
                'UpdateLicense' {
                    Write-Host "Loading Instance-data..."
                    Import-Module Cloud.Ready.Software.NAV -Force
                    Import-NAVModules

                    # Download License
                    $targetFilename = 'C:\Install\ScriptDownload\license.flf'
                    Download-CustomFile -URI $row.Parameter1 -DestinationFile $targetFilename

                    $environments = Get-EnvironmentsFromStorage -StorageAccountContext $storageAccountContext -TableNameEnvironments $StorageTableNameEnvironments -TableNameDefaults $StorageTableNameEnvironmentDefaults -TypeFilter $row.Parameter1 -ConfigType Application
                    foreach ($environment in $environments) {
                        if (Get-NavServerInstance -ServerInstance $environment.ServerInstance) {
                            Write-Host "Updating license..."
                            Import-NAVServerLicense -ServerInstance $environment.ServerInstance -LicenseFile $targetFilename
                            if ($row.Parameter2 -eq "RestartService") {
                                Write-Host "Restarting service..."
                                Restart-NAVServerInstance -ServerInstance $environment.ServerInstance | Out-Null
                            }
                        }
                    }                    
                }
                'CreateWebInstances' {
                    Write-Host "Importing Cloud.Ready.Software.NAV..."
                    Import-Module Cloud.Ready.Software.NAV -Force
                    Write-Host "Importing NAV Modules..."
                    # On the WebServer-VM there is no complete NAV/BC installation, so we need to only load the relevant WebClient-module
                    #Import-NAVModules
                    $path = "C:\Program Files\Microsoft Dynamics *\*\Web Client\Modules\NAVWebClientManagement\NAVWebClientManagement.psm1"
                    $modulePath = (Get-ChildItem -Path $path | Select-Object -First 1).FullName
                    Import-Module -Name $modulePath
                    
                    $infrastructureData = Get-InfrastructureDataFromStorage -StorageAccountContext $storageAccountContext -TableNameInfrastructureData $StorageTableNameInfrastructureData
                    $environments = Get-EnvironmentsFromStorage -StorageAccountContext $storageAccountContext -TableNameEnvironments $StorageTableNameEnvironments -TableNameDefaults $StorageTableNameEnvironmentDefaults -TypeFilter $row.Parameter1 -ConfigType Application
                    foreach ($environment in $environments) {
                        $serverInstanceName = "$($environment.ServerInstance)-Web"
                        if (-not (Get-NAVWebServerInstance -WebServerInstance $serverInstanceName)) { 
                            Write-Host "Creating instance: $serverInstanceName"
                            $params = @{
                                WebServerInstance            = $serverInstanceName
                                Server                       = $infrastructureData.ApplicationServerLoadBalancerIP
                                ServerInstance               = $environment.ServerInstance
                                ClientServicesCredentialType = $environment.Authentication
                                ClientServicesPort           = $environment.ClientServicesPort
                            }
                            if (-not([string]::IsNullOrEmpty($infrastructureData.DnsIdentity))){
                                $params.Add("DnsIdentity", $infrastructureData.DnsIdentity)
                            }
                            New-NAVWebServerInstance @params                            
                            Write-Host "Updating navsettings.json..."
                            $configFile = "C:\inetpub\wwwroot\$($serverInstanceName)\navsettings.json"
                            $a = Get-Content $configFile -raw | ConvertFrom-Json        
                            $a.NAVWebSettings.ManagementServicesPort = $environment.ManagementServicesPort
                            $a | ConvertTo-Json -depth 32 | set-content $configFile
                        }
                    }
                }
                # Add "CreateWebInstances"
                # Add "UpdateWebInstanceConfiguration"
                # Add "UpdateCertificate"
                # Add "UpdateWebCertificate"
                # Add "CreateDelegation"
                # Add "CreateSPN"
                default {
                    Write-Host "Not implemented yet."
                }                
            }
            if ($row.RestartNecessary -eq $false) {
                # Update Command Table, because machine will be restartet in a moment
                Set-StorageCommandExecuted -CommandRow $row -ExecutedByName $env:computername
            }
            if ($row.RestartNecessary -eq $true) {
                break       
            }
        }
    }
}