By Steve Endow
UPDATE: I have posted version 3 of the script here
In July 2020 I posted an earlier version of this script. This updated version has a few refinements, such as removing and importing BcContainerHelper to ensure the latest version is being used, adding Flush-ContainerHelperCache to remove old helper files, and removing old Docker images.
Automate, Automate, Automate |
I setup a "Dev Container" task in Windows Task Scheduler to run this script every morning at 6:45am so that I always have a fresh BC container based on the latest BC artifacts. I call my main Container "dev17", based on the current 17.x version of Business Central. Do NOT name your container just "dev"--see my video here on why that will cause problems.
And you'll notice that I have a second "container TEST" task setup in Windows Task Scheduler that creates a second Container, called test17. If for some reason dev17 isn't working or wasn't created properly, I can usually rely on test17 to be working. I just did a livestream that demonstrated why this is valuable--my dev17 container build script failed due to a download error, but I was able to easily work with my test17 container.
Changes you will probably want to make to my sample script:
1. Optionally change the container name to suit your preference
2. Set the DNS IP to your preferred DNS server. I have an internal Pi-Hole DNS server, but you will likely use an external DNS server, such as 8.8.8.8
3. Change the path for your transcript files to a drive & directory that works for you
4. Change the email addresses and SMTP server
5. Create a secure string password file containing your email password using the included command
6. Set your container BC password if you use something other than admin and P@ssw0rd to login
7. Change the parameters for the New-BcContainer command to meet your needs (e.g. license file, includeTestToolkit, etc.
Below is version 2.1 of the script, as of February 2021.
#v2.1 - February 21, 2021
#
#Define the host, container, and image names
$server = $env:COMPUTERNAME
$containerName = "dev17"
$imageName = "i" + $containerName
$dnsIP = "8.8.8.8"
#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"
#Specify file location for activity transcript log file
$transcriptFile = "D:\BCPowerShell\Logs\" + $fileDateTime + " " + $containerName + " Container Build Log.txt"
#Start recording transcript
Start-Transcript -Path $transcriptFile
#Record the start time
$StartTime = $(get-date)
#Define email configuration
$emailFrom = "youremail@gmail.com"
$emailTo = "youremail@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_password.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 {
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 #-version 16.5 #-type 'Sandbox' -version '' -country 'us' -select 'Latest'
New-BcContainer `
-accept_eula `
-containerName $containerName `
-credential $credential `
-auth $auth `
-artifactUrl $artifactUrl `
-dns $dnsIP `
-updateHosts `
-licenseFile "D:\BCLicense\BCv17.flf" `
-assignPremiumPlan `
-includeTestToolkit `
-includeAL `
-imageName $imageName
}
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 {
#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.
No comments:
Post a Comment
All comments must be reviewed and approved before being published. Your comment will not appear immediately.