By Steve Endow
I created this PowerShell script to automatically re-create my Business Central Docker Containers every morning.
Version 3 of the script records BC Image version information to a log file and to the notification email.
(Thanks to Kishor Mistry for the improved method to get the BC image version!)
Email notification with full log |
#v3.0 - June 17, 2021
#
#Define the host, container, and image names
$server = $env:COMPUTERNAME
$containerName = "dev18"
$imageName = "i" + $containerName
$dnsIP = "192.168.25.21"
#Define date values
$simpleDate = Get-Date -Format "M/d/yy"
$dateTime = Get-Date -Format "M/d/yy HH:mm"
$fileDateTime = Get-Date -Format "yyyy-MM-dd HHmm"
$currentDate = Get-Date -Format "yyyy-MM-dd"
$currentTime = Get-Date -Format "hh:mm:ss"
#Specify file location for activity transcript log file
$transcriptFile = "D:\BCPowerShell\Logs\" + $fileDateTime + " " + $containerName + " Container Build Log.txt"
#File location for BC version tracking
$versionFile = "D:\BCPowerShell\Logs\" + $containerName + " BC Version Log.txt"
#Start recording transcript
Start-Transcript -Path $transcriptFile
#Record the start time
$StartTime = $(get-date)
#Define email configuration
$emailFrom = "precipioazure@gmail.com"
$emailTo = "steveendow@gmail.com"
$smtpServer = "smtp.gmail.com"
$port = "587"
#Email password file created using: Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath D:\BCPowerShell\Gmail.securestring
$passwordFile = "D:\BCPowerShell\Gmail_sendow.securestring"
$securePassword = ConvertTo-SecureString (Get-Content -Path $passwordFile)
$smtpCred = New-Object -TypeName PSCredential ($emailFrom, $securePassword)
$subject = $server + ": " + $simpleDate + " BC Build Log for Container: " + $containerName
$started = "Start time: " + $StartTime
$body = $started + "`n"
$errFlag = $false
$warnFlag = $false
#Output startup info
Write-Output $subject
Write-Output $started
Try {
#********************************************************************************************
#Get image version from current container
try {
$currentImageNum = (Get-BcContainerAppInfo -containerName $ContainerName | Where-Object -Property Publisher -EQ "Microsoft" | Where-Object -Property Name -EQ "Application").Version.ToString()
}
catch
{
$currentImageNum = 'No Container'
}
#********************************************************************************************
Remove-Module BcContainerHelper -ErrorAction SilentlyContinue
Import-Module BcContainerHelper -ErrorAction SilentlyContinue
#Remove old artifacts and images
Flush-ContainerHelperCache -cache bcartifacts -keepDays 7
#Remove existing container if it exists
Remove-BCContainer $containerName
#Remove old images without deleting base OS image
docker images --format "{{.Repository}}\t{{.Tag}}\t{{.ID}}" |
Select-String "businesscentral" -notMatch |
ConvertFrom-CSV -Delimiter "`t" -Header ("Repository","Tag","ID") |
Sort-Object Tag | % ID | % { docker rmi $_ }
#Call New-BcContainer - Use New-BCContainerWizard to generate the script/params for your environment
$password = 'P@ssw0rd'
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$credential = New-Object pscredential 'admin', $securePassword
$auth = 'UserPassword'
$artifactUrl = Get-BcArtifactUrl -country us -select Latest -type Sandbox
New-BcContainer `
-accept_eula `
-containerName $containerName `
-credential $credential `
-auth $auth `
-artifactUrl $artifactUrl `
-dns $dnsIP `
-updateHosts `
-licenseFile "D:\BCPowerShell\BCLicense\BCv18.flf" `
-assignPremiumPlan `
-includeTestToolkit `
-includeAL `
-shortcuts None `
-imageName $imageName
Publish-BcContainerApp -appFile "D:\zDisks\BCCL\BCCL_1.15.0.125\installer\Extension\Hougaard.com_Business Central Command Line_1.15.0.125.app" -containerName dev18 -install -packageType Extension -sync -syncMode Add
}
Catch {
#If the script fails or has an error, output to error stream
Write-Error "Um, something didn't go well:"
Write-Error $_
#Capture error in email body
$body += "`nERROR:`n`n" + $_ + "`n`n"
$errFlag = $true
}
Finally {
#********************************************************************************************
#Get image version info from new container
try {
$newImageNum = (Get-BcContainerAppInfo -containerName $ContainerName | Where-Object -Property Publisher -EQ "Microsoft" | Where-Object -Property Name -EQ "Application").Version.ToString()
}
catch
{
$newImageNum = 'No Container'
}
#Create line to write to BC Version log file
$versionEntry = $currentDate.ToString() + "`t" + $currentTime.ToString() + "`t" + $currentImageNum + "`t" + $newImageNum
#Write entry to BC Version log file
Add-Content -Path $versionFile -Value $versionEntry
#Add BC Versions to email body
$body += "`n" + "Prior BC version: " + $currentImageNum
$body += "`n" + "New BC version: " + $newImageNum + "`n"
#********************************************************************************************
#PROTOTYPE - This is fairly fragile and needs improvement
#Search for the Container OS Build number and compare it to the Host OS build number
[string]$containerOSVersion = Select-String -Pattern "Container OS Version: " -SimpleMatch -Path $transcriptFile
$trimBefore = $containerOSVersion.IndexOf("Version: ")
#This is pretty crude parsing, so there is presumably a more elegant method
$containerOSBuild = $containerOSVersion.Substring($trimBefore+9, ($containerOSVersion.Length-($trimBefore+9))).Split(" (")[0].Split(".")[3]
$osVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name UBR).UBR
If ($containerOSBuild -ne $osVersion)
{
$warnFlag = $true
$buildWarning = "`nWARNING: Container OS Build " + $containerOSBuild + " does NOT match Host OS Build " + $osVersion
Write-Error $buildWarning
$body += "`n" + $buildWarning + "`n"
}
Else
{
$output = "Container OS Build " + $containerOSBuild + " matches Host OS Build " + $osVersion
Write-Output $output
$body += "`n" + $output + "`n"
}
ipconfig /flushdns;
#Record end time
$EndTime = $(get-date)
$output = "End time: " + $EndTime
$body += "`n" + $output
Write-Output $output
#Calculate elapsed time
$elapsedTime = $EndTime - $StartTime
$totalTime = "{0:HH:mm:ss}" -f ([datetime]$elapsedTime.Ticks)
$output = "Elapsed time: " + $totalTime
$body += "`n" + $output
Write-Output $output
$body += "`n`nFull log file is attached"
If ($errFlag)
{
$subject = "ERROR: " + $subject
$greeting = "Greetings human.`n`nIt appears there was an error building your Business Central Docker Container: " + $containerName + "`n`n"
}
elseif ($warnFlag)
{
$subject = "WARNING: " + $subject
$greeting = "Greetings human.`n`nI prepared your Business Central Docker Container: " + $containerName + ", but detected potential issues.`n`n"
}
else
{
$greeting = "Greetings human.`n`nI have prepared your Business Central Docker Container: " + $containerName + "`n`n"
}
$body = $greeting + $body
#Finish the transcript recording
#The Transcript must be stopped before trying to send the email,
#otherwise the log file will be locked, causing the email to fail
Stop-Transcript
#Send the email
Send-MailMessage -From $emailFrom -To $emailTo `
-Subject $subject `
-Body $body `
-Attachments $transcriptFile `
-SmtpServer $smtpServer `
-Credential $smtpCred `
-Port $port `
-UseSsl
}
Steve Endow is a Microsoft MVP in Los Angeles. He works with Dynamics 365 Business Central, Microsoft Power Automate, Power Apps, Azure, and .NET.
You can also find him on Twitter and YouTube
No comments:
Post a Comment
All comments must be reviewed and approved before being published. Your comment will not appear immediately.