├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── Image-Factory-Deploy.ps1 └── Image-Factory.ps1 /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [digressive] 2 | custom: ["https://www.paypal.me/digressive"] 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2024 Mike Galvin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Image Factory Utility 2 | 3 | ## Automate Creation of WIM Files 4 | 5 | For full change log and more information, [visit my site.](https://gal.vin/utils/image-factory-utility/) 6 | 7 | Image Factory Utility is available from: 8 | 9 | * [GitHub](https://github.com/Digressive/Image-Factory) 10 | * [The Microsoft PowerShell Gallery](https://www.powershellgallery.com/packages/Image-Factory) 11 | 12 | Please consider supporting my work: 13 | 14 | * Support with [Github Sponsors](https://github.com/sponsors/Digressive). 15 | * Support with a one-time donation using [PayPal](https://www.paypal.me/digressive). 16 | 17 | Please report any problems via the ‘issues’ tab on GitHub. 18 | 19 | Thanks 20 | -Mike 21 | 22 | ## Features and Requirements 23 | 24 | * This utility is designed to run on a computer with Microsoft Deployment Toolkit installed. 25 | * The computer must have either the Hyper-V management PowerShell modules installed, or Virtual Box. 26 | * The primary function of this utility is to automate the creation of wim files from MDT task sequences. 27 | * The utility requires at least PowerShell 5.0. 28 | * This utility has been tested on Windows 11, Windows 10, Windows Server 2019, Windows Server 2016 and Windows Server 2012 R2. 29 | 30 | ## Virtual Box Support 31 | 32 | This utility expects Oracle Virtual Box to be installed in the default location of: ```C:\Program Files\Oracle\VirtualBox``` 33 | 34 | ## Important Information 35 | 36 | The utility will make changes to your customsettings.ini file, although it will make a backup first. These changes are necessary so that the build process runs automated. Depending on your environment, you may need to make additional changes to your customsettings.ini. 37 | 38 | ## Separating your build and deployment shares 39 | 40 | I would recommend running with a separate build share so that: 41 | 42 | * The Image Factory Utility doesn't tie up the main deployment share whilst running. 43 | * The build environment can be configured separately. 44 | * The boot media for the build share can be configured to automatically log into the deployment environment. 45 | 46 | Here are the settings you'll need to add to your Bootstrap.ini to automatically log into the build share. Don't forget to update your build share in MDT and regenerate the boot images. 47 | 48 | ```txt 49 | [Settings] 50 | Priority=Default 51 | 52 | [Default] 53 | DeployRoot=[path\]BuildShare$ 54 | UserDomain=contoso.com 55 | UserID=mdt_admin 56 | UserPassword=P@ssw0rd 57 | SkipBDDWelcome=YES 58 | ``` 59 | 60 | ## Generating A Password File For SMTP Authentication 61 | 62 | The password used for SMTP server authentication must be in an encrypted text file. To generate the password file, run the following command in PowerShell on the computer and logged in with the user that will be running the utility. When you run the command, you will be prompted for a username and password. Enter the username and password you want to use to authenticate to your SMTP server. 63 | 64 | Please note: This is only required if you need to authenticate to the SMTP server when send the log via e-mail. 65 | 66 | ``` powershell 67 | $creds = Get-Credential 68 | $creds.Password | ConvertFrom-SecureString | Set-Content c:\scripts\ps-script-pwd.txt 69 | ``` 70 | 71 | After running the commands, you will have a text file containing the encrypted password. When configuring the -Pwd switch enter the path and file name of this file. 72 | 73 | ## Configuration 74 | 75 | Here’s a list of all the command line switches and example configurations. 76 | 77 | | Command Line Switch | Description | Example | 78 | | ------------------- | ----------- | ------- | 79 | | -Build | Location of the build share. It can be the same as the deployment share, and it can be a local or UNC path. | [path\] | 80 | | -Deploy | Location of the deployment share. It can be the same as the deployment share, and it can be a local or UNC path. | [path\] | 81 | | -Vh | Hyper-V Only - Name of the Hyper-V host if remote. If not set it will default to local. | [hostname] | 82 | | -Vhd | The path to store the virtual hard disk file(s). If using a remote Hyper-V server the path should be relative for that server. | [path\] | 83 | | -Boot | The path to the iso file to boot from. If using a remote Hyper-V server the path should be relative for that server. | [path\]LiteTouchPE_x64.iso | 84 | | -Vnic | Hyper-V Only - Name of the virtual switch that the VM should use to communicate. | "'virtual NIC name'" | 85 | | -Ts | The comma-separated list of task sequence ID's to build. | [W11-21H2,W10-21H2] | 86 | | -VBox | Use this switch to use Oracle Virtual Box instead of Hyper-V | N/A | 87 | | -Compat | Legacy Hyper-V Only - Use this switch if the Hyper-V host is Windows Server 2012 R2 and the script is running on Windows 10 or Windows Server 2016/2019. This loads the older version of the Hyper-V module, so it can manage WS2012 R2 Hyper-V VMs. | N/A | 88 | | -Remote | Hyper-V Only - Use this switch if the Hyper-V server is a remote device. | N/A | 89 | | -L | The path to output the log file to. | [path\] | 90 | | -LogRotate | Remove logs produced by the utility older than X days | [number] | 91 | | -NoBanner | Use this option to hide the ASCII art title in the console. | N/A | 92 | | -Help | Display usage information. No arguments also displays help. | N/A | 93 | | -ProgCheck | Send notifications (email or webhook) after each Task Sequence is processed. | N/A | 94 | | -Subject | Specify a subject line. If you leave this blank the default subject will be used | "'[Server: Notification]'" | 95 | | -SendTo | The e-mail address the log should be sent to. For multiple address, separate with a comma. | [example@contoso.com] | 96 | | -From | The e-mail address the log should be sent from. | [example@contoso.com] | 97 | | -Smtp | The DNS name or IP address of the SMTP server. | [smtp server address] | 98 | | -Port | The Port that should be used for the SMTP server. If none is specified then the default of 25 will be used. | [port number] | 99 | | -User | The user account to authenticate to the SMTP server. | [example@contoso.com] | 100 | | -Pwd | The txt file containing the encrypted password for SMTP authentication. | [path\]ps-script-pwd.txt | 101 | | -UseSsl | Configures the utility to connect to the SMTP server using SSL. | N/A | 102 | 103 | ## Example 104 | 105 | ``` txt 106 | [path\]Image-Factory.ps1 -Build [path\] -Deploy [path\] -Boot [path\]LiteTouchPE_x64.iso -Vnic [virtual NIC name] -Ts W11-21H2,W10-21H2 107 | ``` 108 | 109 | This will use Hyper-V VMs on the local machine to build wim files from the task sequences W11-21H2 and W10-21H2. The wim files will be imported to the deployment share specified. 110 | 111 | ## Change Log 112 | 113 | ### 2023-09-01: Version 23.09.01 114 | 115 | * Added -ProgCheck option. With this option set, notifications will be sent after each Task Sequence is processed. 116 | 117 | ### 2023-04-28: Version 23.04.28 118 | 119 | * Removed specific SMTP config info from config report. 120 | * Added script update checker - shows if an update is available in the log and console. If the internet is not reachable it silently errors out. 121 | 122 | ### 2022-06-18: Version 22.06.18 123 | 124 | * Fixed Get-Service check outputting to console. 125 | 126 | ### 2022-06-17: Version 22.06.17 127 | 128 | * Fixed an issue with Windows Server 2012 R2 when checking for the Hyper-V service to be installed and running. 129 | 130 | ### 2022-06-14: Version 22.06.07 131 | 132 | * Added new feature: log can now be emailed to multiple addresses. 133 | * Added checks and balances to help with configuration as I'm very aware that the initial configuration can be troublesome. Running the utility manually is a lot more friendly and step-by-step now. 134 | * Added -Help to give usage instructions in the terminal. Running the script with no options will also trigger the -help switch. 135 | * Cleaned user entered paths so that trailing slashes no longer break things or have otherwise unintended results. 136 | * Added -LogRotate [days] to removed old logs created by the utility. 137 | * Streamlined config report so non configured options are not shown. 138 | * Added donation link to the ASCII banner. 139 | * Cleaned up code, removed unneeded log noise. 140 | 141 | ### 2021-12-02: Version 21.12.01 142 | 143 | * Added option to use Oracle Virtual Box instead of Hyper-V. 144 | * Configured logs path now is created, if it does not exist. 145 | * Added OS version info. 146 | * Added an option to specify the Port for SMTP communication. 147 | 148 | ### 2021-06-22: Version 21.06.22 149 | 150 | * Added a progression bar display. 151 | * Changed a variable to prevent conflicts with future PowerShell versions. 152 | 153 | ### 2020-02-27: Version 20.02.24 ‘Robot’ 154 | 155 | New features: 156 | 157 | * Refactored code. 158 | * Fully backwards compatible. 159 | * Added ASCII banner art when run in the console. 160 | * Added option to disable the ASCII banner art. 161 | 162 | ### 2019-09-04 v2.9 163 | 164 | * Added custom subject line for e-mail. 165 | 166 | ### 2018-12-17 v2.8 167 | 168 | * The script will now set automatic checkpoints to 'disabled' on the VM's. This is to help with VM disk management and clean up. 169 | 170 | ### 2017-10-16 v2.7 171 | 172 | * Changed SMTP authentication to require an encrypted password file. 173 | * Added instructions on how to generate an encrypted password file. 174 | 175 | ### 2017-10-09 v2.6 176 | 177 | * Added necessary information to add the script to the PowerShell Gallery. 178 | 179 | ### 2017-09-18 v2.5 180 | 181 | * Added a sanity check of the MDT deployment share. The script now checks for an existing CustomSettings-backup.ini file. If it exists, it reports that the deployment share is not clean. 182 | * Added extra line breaks when editing the CustomSettings.ini as previously it was adding the required configuration on the last line of the ini file and causing the deployment to fail. Many thanks to Twitter user [@thestardawg](https://twitter.com/thestardawg) for reporting this bug. 183 | 184 | ### 2017-08-26 v2.4 185 | 186 | * Improved logging so that the log file and console output is now more readable. 187 | 188 | ### 2017-07-22 v2.3 189 | 190 | * Improved commenting on the code for documentation purposes. 191 | * Added authentication and SSL options for e-mail notification. 192 | 193 | ### 2017-05-11 v2.2 194 | 195 | * Added command line configuration options so the script itself does not need to be edited. 196 | * Added code to manage the Virtual Machines without the need for extra configuration options. 197 | * Removed some unnecessary extra configuration options and variables. 198 | 199 | ### 2017-04-25 v2.1 200 | 201 | I've added logging to the script and the ability to email the log on completion. I've also added a variable to configure the Virtual Switch that the VM's Network Adaptor should use. This was an oversight on the previous version. 202 | 203 | ### 2017-04-17 Minor update 204 | 205 | I've added hour and minutes to the WIM file creation name as I have been running multiple images of the same Task Sequence within a day and needed some extra data to prevent the image from over writing the previous one. I've also made another script, using this one as a base so I can generate VMs to test the deployment of the captured images after I've manually renamed them in MDT and added to the task sequences. The VMs are named after the Task Sequence ID and do not delete after the Task Sequence completes. 206 | -------------------------------------------------------------------------------- /Image-Factory-Deploy.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 23.09.01 4 | 5 | .GUID 849ea0c5-1c44-49c1-817e-fd7702b83752 6 | 7 | .AUTHOR Mike Galvin Contact: digressive@outlook.com 8 | 9 | .COMPANYNAME Mike Galvin 10 | 11 | .COPYRIGHT (C) Mike Galvin. All rights reserved. 12 | 13 | .TAGS Microsoft Deployment Toolkit MDT Hyper-V Windows OSD Testing 14 | 15 | .LICENSEURI https://github.com/Digressive/Image-Factory?tab=MIT-1-ov-file 16 | 17 | .PROJECTURI https://gal.vin/utils/image-factory-utility/ 18 | 19 | .ICONURI 20 | 21 | .EXTERNALMODULEDEPENDENCIES 22 | 23 | .REQUIREDSCRIPTS 24 | 25 | .EXTERNALSCRIPTDEPENDENCIES 26 | 27 | .RELEASENOTES 28 | 29 | #> 30 | 31 | <# 32 | .SYNOPSIS 33 | Image Factory Utility (Deploy) - Automate testing of WIM files and task sequences. 34 | 35 | .DESCRIPTION 36 | This script will create Hyper-V virtual machines to test WIM files and Microsoft Deployment 37 | Toolkit task sequences. 38 | Run with -help or no arguments for usage. 39 | #> 40 | 41 | ## Set up command line switches. 42 | [CmdletBinding()] 43 | Param( 44 | [alias("Deploy")] 45 | $MdtDeployPathUsr, 46 | [alias("TS")] 47 | $TsId, 48 | [alias("VH")] 49 | $VmHost, 50 | [alias("VHD")] 51 | $VhdPathUsr, 52 | [alias("Boot")] 53 | $BootMedia, 54 | [alias("VNic")] 55 | $VmNic, 56 | [alias("L")] 57 | $LogPathUsr, 58 | [alias("LogRotate")] 59 | $LogHistory, 60 | [alias("Subject")] 61 | $MailSubject, 62 | [alias("SendTo")] 63 | $MailTo, 64 | [alias("From")] 65 | $MailFrom, 66 | [alias("Smtp")] 67 | $SmtpServer, 68 | [alias("Port")] 69 | $SmtpPort, 70 | [alias("User")] 71 | $SmtpUser, 72 | [alias("Pwd")] 73 | [ValidateScript({Test-Path -Path $_ -PathType Leaf})] 74 | $SmtpPwd, 75 | [switch]$UseSsl, 76 | [switch]$Remote, 77 | [switch]$VBox, 78 | [switch]$Help, 79 | [switch]$ProgCheck, 80 | [switch]$NoBanner) 81 | 82 | If ($NoBanner -eq $False) 83 | { 84 | Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " 85 | .___ ___________ __ ____ ___ __ .__.__ .__ __ 86 | | | _____ ____ \_ _____/____ _____/ |_ ___________ ___.__. | | \_/ |_|__| | |__|/ |_ ___.__. 87 | | |/ \ / ___\ | __) \__ \ _/ ___\ __\/ _ \_ __ < | | | | /\ __\ | | | \ __< | | 88 | | | Y Y \/ /_/ > | \ / __ \\ \___| | ( <_> ) | \/\___ | | | / | | | | |_| || | \___ | 89 | |___|__|_| /\___ / \___ / (____ /\___ >__| \____/|__| / ____| |______/ |__| |__|____/__||__| / ____| 90 | \//_____/ \/ \/ \/ \/ \/ 91 | Mike Galvin Version 23.09.01 92 | https://gal.vin See -help for usage -Deploy- 93 | Donate: https://www.paypal.me/digressive 94 | " 95 | } 96 | 97 | If ($PSBoundParameters.Values.Count -eq 0 -or $Help) 98 | { 99 | Write-Host -Object " Usage: 100 | 101 | From a terminal run: [path\]Image-Factory-Deploy.ps1 -Deploy [path\] -Boot [path\]LiteTouchPE_x64-Deploy.iso 102 | -Vnic [virtual NIC name] -Ts W11-21H2,W10-21H2 103 | 104 | This will use Hyper-V VMs on the local machine to build wim files from the task sequences W11-21H2 and W10-21H2. 105 | 106 | Use -VH [hostname] to specify a remote Hyper-V server. 107 | Please note that -Boot and -VHD paths will be local to the remote server. 108 | 109 | Use -VHD [path\] to configure where to store the VM's VHD, if not the default. 110 | 111 | Use -Remote when the Hyper-V server is a remote computer. 112 | Use -VBox if using Virtual Box instead of Hyper-V as the VM platform. 113 | Use -ProgCheck to send notifications (email or webhook) after each Task Sequence is processed. 114 | 115 | To output a log: -L [path\]. 116 | To remove logs produced by the utility older than X days: -LogRotate [number]. 117 | Run with no ASCII banner: -NoBanner 118 | 119 | To use the 'email log' function: 120 | Specify the subject line with -Subject ""'[subject line]'"" If you leave this blank a default subject will be used 121 | Make sure to encapsulate it with double & single quotes as per the example for Powershell to read it correctly. 122 | 123 | Specify the 'to' address with -SendTo [example@contoso.com] 124 | For multiple address, separate with a comma. 125 | 126 | Specify the 'from' address with -From [example@contoso.com] 127 | Specify the SMTP server with -Smtp [smtp server name] 128 | 129 | Specify the port to use with the SMTP server with -Port [port number]. 130 | If none is specified then the default of 25 will be used. 131 | 132 | Specify the user to access SMTP with -User [example@contoso.com] 133 | Specify the password file to use with -Pwd [path\]ps-script-pwd.txt. 134 | Use SSL for SMTP server connection with -UseSsl. 135 | 136 | To generate an encrypted password file run the following commands 137 | on the computer and the user that will run the script: 138 | " 139 | Write-Host -Object ' $creds = Get-Credential 140 | $creds.Password | ConvertFrom-SecureString | Set-Content [path\]ps-script-pwd.txt' 141 | } 142 | 143 | else { 144 | ## If logging is configured, start logging. 145 | ## If the log file already exists, clear it. 146 | If ($LogPathUsr) 147 | { 148 | ## Clean User entered string 149 | $LogPath = $LogPathUsr.trimend('\') 150 | 151 | ## Make sure the log directory exists. 152 | If ((Test-Path -Path $LogPath) -eq $False) 153 | { 154 | New-Item $LogPath -ItemType Directory -Force | Out-Null 155 | } 156 | 157 | $LogFile = ("Image-Factory-Deploy_{0:yyyy-MM-dd_HH-mm-ss}.log" -f (Get-Date)) 158 | $Log = "$LogPath\$LogFile" 159 | 160 | If (Test-Path -Path $Log) 161 | { 162 | Clear-Content -Path $Log 163 | } 164 | } 165 | 166 | ## Function to get date in specific format. 167 | Function Get-DateFormat 168 | { 169 | Get-Date -Format "yyyy-MM-dd HH:mm:ss" 170 | } 171 | 172 | ## Function for logging. 173 | Function Write-Log($Type, $Evt) 174 | { 175 | If ($Type -eq "Info") 176 | { 177 | If ($LogPathUsr) 178 | { 179 | Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [INFO] $Evt" 180 | } 181 | 182 | Write-Host -Object " $(Get-DateFormat) [INFO] $Evt" 183 | } 184 | 185 | If ($Type -eq "Succ") 186 | { 187 | If ($LogPathUsr) 188 | { 189 | Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [SUCCESS] $Evt" 190 | } 191 | 192 | Write-Host -ForegroundColor Green -Object " $(Get-DateFormat) [SUCCESS] $Evt" 193 | } 194 | 195 | If ($Type -eq "Err") 196 | { 197 | If ($LogPathUsr) 198 | { 199 | Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [ERROR] $Evt" 200 | } 201 | 202 | Write-Host -ForegroundColor Red -BackgroundColor Black -Object " $(Get-DateFormat) [ERROR] $Evt" 203 | } 204 | 205 | If ($Type -eq "Conf") 206 | { 207 | If ($LogPathUsr) 208 | { 209 | Add-Content -Path $Log -Encoding ASCII -Value "$Evt" 210 | } 211 | 212 | Write-Host -ForegroundColor Cyan -Object " $Evt" 213 | } 214 | } 215 | 216 | ## Function for Notifications 217 | Function Notify() 218 | { 219 | ## This whole block is for e-mail, if it is configured. 220 | If ($SmtpServer) 221 | { 222 | If (Test-Path -Path $Log) 223 | { 224 | ## Default e-mail subject if none is configured. 225 | If ($Null -eq $MailSubject) 226 | { 227 | $MailSubject = "Image Factory Utility Log" 228 | } 229 | 230 | ## Default Smtp Port if none is configured. 231 | If ($Null -eq $SmtpPort) 232 | { 233 | $SmtpPort = "25" 234 | } 235 | 236 | ## Setting the contents of the log to be the e-mail body. 237 | $MailBody = Get-Content -Path $Log | Out-String 238 | 239 | ForEach ($MailAddress in $MailTo) 240 | { 241 | ## If an smtp password is configured, get the username and password together for authentication. 242 | ## If an smtp password is not provided then send the e-mail without authentication and obviously no SSL. 243 | If ($SmtpPwd) 244 | { 245 | $SmtpPwdEncrypt = Get-Content $SmtpPwd | ConvertTo-SecureString 246 | $SmtpCreds = New-Object System.Management.Automation.PSCredential -ArgumentList ($SmtpUser, $SmtpPwdEncrypt) 247 | 248 | ## If -ssl switch is used, send the email with SSL. 249 | ## If it isn't then don't use SSL, but still authenticate with the credentials. 250 | If ($UseSsl) 251 | { 252 | Send-MailMessage -To $MailAddress -From $MailFrom -Subject "$MailSubject $Succi/$($TsId.count) TSs Successful" -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpPort -UseSsl -Credential $SmtpCreds 253 | } 254 | 255 | else { 256 | Send-MailMessage -To $MailAddress -From $MailFrom -Subject "$MailSubject $Succi/$($TsId.count) TSs Successful" -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpPort -Credential $SmtpCreds 257 | } 258 | } 259 | 260 | else { 261 | Send-MailMessage -To $MailAddress -From $MailFrom -Subject "$MailSubject $Succi/$($TsId.count) TSs Successful" -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpPort 262 | } 263 | } 264 | } 265 | else { 266 | Write-Host -ForegroundColor Red -BackgroundColor Black -Object " There's no log file to email." 267 | } 268 | } 269 | ## End of Email block 270 | } 271 | 272 | ## Function for Update Check 273 | Function UpdateCheck() 274 | { 275 | $ScriptVersion = "23.09.01" 276 | $RawSource = "https://raw.githubusercontent.com/Digressive/Image-Factory/master/Image-Factory-Deploy.ps1" 277 | 278 | try { 279 | $SourceCheck = Invoke-RestMethod -uri "$RawSource" 280 | $VerCheck = $SourceCheck -split '\n' | Select-String -Pattern ".VERSION $ScriptVersion" -SimpleMatch -CaseSensitive -Quiet 281 | 282 | If ($VerCheck -ne $True) 283 | { 284 | Write-Log -Type Conf -Evt "*** There is an update available. ***" 285 | } 286 | } 287 | 288 | catch { 289 | } 290 | } 291 | 292 | ## Config checks for conflicting options, needless options. 293 | If ($Null -eq $MdtDeployPathUsr) 294 | { 295 | Write-Log -Type Err -Evt "The Deployment share is not specified." 296 | Exit 297 | } 298 | 299 | If ($Null -eq $TsId) 300 | { 301 | Write-Log -Type Err -Evt "No Task Sequence IDs are specified." 302 | Exit 303 | } 304 | 305 | If ($Null -eq $BootMedia) 306 | { 307 | Write-Log -Type Err -Evt "The boot media is not specified." 308 | Exit 309 | } 310 | 311 | If ($Null -eq $VmNic) 312 | { 313 | If ($Vbox -eq $false) 314 | { 315 | Write-Log -Type Err -Evt "The virtual NIC is not specified." 316 | Exit 317 | } 318 | } 319 | 320 | else { 321 | If ($Vbox -eq $true) 322 | { 323 | Write-Log -Type Info -Evt "This setting is ignored with Virtual Box." 324 | Exit 325 | } 326 | } 327 | 328 | If ($Null -ne $Vmhost -AND $Vbox -eq $True) 329 | { 330 | Write-Log -Type Err -Evt "The VM host does not need to be configured with the -VBox switch." 331 | Exit 332 | } 333 | 334 | ## If not configured set VmHost to local 335 | If ($Null -eq $VmHost) 336 | { 337 | If ($Remote) 338 | { 339 | If ($Vbox) 340 | { 341 | Write-Log -Type Err -Evt "The -Remote switch has no effect with Virtual Box, Virtual Box must be installed locally." 342 | Exit 343 | } 344 | 345 | Write-Log -Type Err -Evt "You must specify the remote VM host when the -Remote switch is set." 346 | Exit 347 | } 348 | 349 | $VmHost = $Env:ComputerName 350 | 351 | If ($Vbox) 352 | { 353 | $VBoxLoc = "C:\Program Files\Oracle\VirtualBox" 354 | 355 | If ((Test-Path -Path $VBoxLoc) -eq $False) 356 | { 357 | Write-Log -Type Err -Evt "Virtual Box is not installed on this local machine." 358 | Exit 359 | } 360 | } 361 | 362 | else { 363 | ## Test for Hyper-V feature installed on local machine. 364 | try { 365 | If ($OSV -eq "6.3.9600") 366 | { 367 | Get-Service vmms -ErrorAction Stop | Out-Null 368 | } 369 | 370 | else { 371 | Get-Service vmcompute -ErrorAction Stop | Out-Null 372 | } 373 | } 374 | 375 | catch { 376 | Write-Log -Type Err -Evt "Hyper-V is not installed on this local machine." 377 | Exit 378 | } 379 | } 380 | } 381 | 382 | else { 383 | If ($Remote -eq $False) 384 | { 385 | Write-Log -Type Err -Evt "You must use the -Remote switch when specifying a remote VM host." 386 | Exit 387 | } 388 | } 389 | 390 | ## If not configured set VhdPath to the default 391 | If ($Null -eq $VhdPathUsr) 392 | { 393 | If ($Vbox -eq $false) 394 | { 395 | $VhdPathUsr = Get-VMHost -Computer $VmHost | Select-Object VirtualHardDiskPath -ExpandProperty VirtualHardDiskPath 396 | } 397 | 398 | else { 399 | Write-Log -Type Err -Evt "You must configure a VHD storage path when using Virtual Box." 400 | Exit 401 | } 402 | } 403 | 404 | If ($Null -eq $LogPathUsr -And $SmtpServer) 405 | { 406 | Write-Log -Type Err -Evt "You must specify -L [path\] to use the email log function." 407 | Exit 408 | } 409 | 410 | ## getting Windows Version info 411 | $OSVMaj = [environment]::OSVersion.Version | Select-Object -expand major 412 | $OSVMin = [environment]::OSVersion.Version | Select-Object -expand minor 413 | $OSVBui = [environment]::OSVersion.Version | Select-Object -expand build 414 | $OSV = "$OSVMaj" + "." + "$OSVMin" + "." + "$OSVBui" 415 | 416 | ## 417 | ## Display the current config and log if configured. 418 | ## 419 | Write-Log -Type Conf -Evt "--- Running with the following config ---" 420 | Write-Log -Type Conf -Evt "Utility Version: 23.09.01" 421 | UpdateCheck ## Run Update checker function 422 | Write-Log -Type Conf -Evt "Hostname: $Env:ComputerName." 423 | Write-Log -Type Conf -Evt "Windows Version: $OSV." 424 | 425 | If ($MdtDeployPathUsr) 426 | { 427 | Write-Log -Type Conf -Evt "Deploy share: $MdtDeployPathUsr." 428 | } 429 | 430 | If ($TsId) 431 | { 432 | Write-Log -Type Conf -Evt "No. of TS ID's: $($TsId.count)." 433 | Write-Log -Type Conf -Evt "TS ID's:" 434 | ForEach ($Id in $TsId) 435 | { 436 | Write-Log -Type Conf -Evt "$Id" 437 | } 438 | } 439 | 440 | If ($VmHost) 441 | { 442 | Write-Log -Type Conf -Evt "VM Host:$VmHost." 443 | } 444 | 445 | If ($VhdPathUsr) 446 | { 447 | Write-Log -Type Conf -Evt "VHD path: $VhdPathUsr." 448 | } 449 | 450 | If ($BootMedia) 451 | { 452 | Write-Log -Type Conf -Evt "Boot media path: $BootMedia." 453 | } 454 | 455 | If ($VmNic) 456 | { 457 | Write-Log -Type Conf -Evt "Virtual NIC name: $VmNic." 458 | } 459 | 460 | If ($LogPathUsr) 461 | { 462 | Write-Log -Type Conf -Evt "Logs directory: $LogPath." 463 | } 464 | 465 | If ($Null -ne $LogHistory) 466 | { 467 | Write-Log -Type Conf -Evt "Logs to keep: $LogHistory days." 468 | } 469 | 470 | If ($MailTo) 471 | { 472 | Write-Log -Type Conf -Evt "E-mail log to: $MailTo." 473 | } 474 | 475 | If ($MailFrom) 476 | { 477 | Write-Log -Type Conf -Evt "E-mail log from: $MailFrom." 478 | } 479 | 480 | If ($MailSubject) 481 | { 482 | Write-Log -Type Conf -Evt "E-mail subject: $MailSubject." 483 | } 484 | 485 | If ($SmtpServer) 486 | { 487 | Write-Log -Type Conf -Evt "SMTP server: Configured" 488 | } 489 | 490 | If ($SmtpUser) 491 | { 492 | Write-Log -Type Conf -Evt "SMTP Auth: Configured" 493 | } 494 | 495 | If ($VBox) 496 | { 497 | Write-Log -Type Conf -Evt "-VBox switch: $VBox." 498 | } 499 | 500 | If ($Remote) 501 | { 502 | Write-Log -Type Conf -Evt "-Remote switch: $Remote." 503 | } 504 | Write-Log -Type Conf -Evt "---" 505 | Write-Log -Type Info -Evt "Process started" 506 | ## 507 | ## Display current config ends here. 508 | ## 509 | 510 | ##Clean the user paths 511 | $VhdPath = $VhdPathUsr.trimend('\') 512 | $MdtDeployPath = $MdtDeployPathUsr.trimend('\') 513 | 514 | ## For Success/Fail stats 515 | $Succi = 0 516 | 517 | ## For Progress bar 518 | $i = 0 519 | 520 | ## 521 | ## For each of the Task Sequence ID's configured, run the build process. 522 | ## 523 | ForEach ($Id in $TsId) 524 | { 525 | ## Progress Bar based on progress through the TS ID's 526 | Write-Progress -Id 0 -Activity "Processing" -Status "Current TSID: $Id" -PercentComplete ($i/$TsId.count*100) 527 | 528 | ## Test to see if the build environment is dirty from another run, if it is exit the script. 529 | If (Test-Path -Path $MdtDeployPath\Control\CustomSettings-backup.ini) 530 | { 531 | Write-Log -Type Err -Evt "CustomSettings-backup.ini already exists." 532 | Write-Log -Type Err -Evt "The build environment is dirty." 533 | Write-Log -Type Err -Evt "Did the script finish successfully last time it was run?" 534 | Exit 535 | } 536 | 537 | Write-Log -Type Info -Evt "Start of Task Sequence ID: $Id" 538 | Write-Log -Type Info -Evt "(TSID:$Id) Backing up current MDT CustomSettings.ini" 539 | 540 | ## Backup the existing CustomSettings.ini. 541 | Copy-Item $MdtDeployPath\Control\CustomSettings.ini $MdtDeployPath\Control\CustomSettings-backup.ini 542 | Start-Sleep -Seconds 5 543 | 544 | Write-Log -Type Info -Evt "(TSID:$Id) Setting MDT CustomSettings.ini for Task Sequence" 545 | 546 | ## Setup MDT CustomSettings.ini for auto deploy. 547 | Add-Content $MdtDeployPath\Control\CustomSettings.ini "" 548 | Add-Content $MdtDeployPath\Control\CustomSettings.ini "" 549 | Add-Content $MdtDeployPath\Control\CustomSettings.ini "TaskSequenceID=$Id" 550 | Add-Content $MdtDeployPath\Control\CustomSettings.ini "SkipTaskSequence=YES" 551 | Add-Content $MdtDeployPath\Control\CustomSettings.ini "SkipComputerName=YES" 552 | 553 | ## Set the VM name as build + the date and time. 554 | $VmName = ("$Id`_{0:yyyy-MM-dd_HH-mm-ss}" -f (Get-Date)) 555 | 556 | Write-Log -Type Info -Evt "(TSID:$Id) Creating VM: $VmName on $VmHost" 557 | Write-Log -Type Info -Evt "(TSID:$Id) Adding VHD: $VhdPath\$VmName.vhdx" 558 | 559 | If ($VmNic) 560 | { 561 | Write-Log -Type Info -Evt "(TSID:$Id) Adding Virtual NIC: $VmNic" 562 | } 563 | 564 | If ($Vbox -eq $false) 565 | { 566 | ## Create the VM with 4GB Dynamic RAM, Gen 1, 127GB VHD, and add the configured vNIC. 567 | try { 568 | New-VM -name $VmName -MemoryStartupBytes 4096MB -BootDevice CD -Generation 1 -NewVHDPath $VhdPath\$VmName.vhdx -NewVHDSizeBytes 130048MB -SwitchName $VmNic -ComputerName $VmHost -ErrorAction Stop | Out-Null 569 | } 570 | 571 | catch { 572 | Write-Log -Type Err -Evt "(TSID:$Id) $_" 573 | 574 | ## Restore CustomSettings.ini from the backup. 575 | Write-Log -Type Info -Evt "(TSID:$Id) Restoring MDT CustomSettings.ini from backup" 576 | Remove-Item $MdtDeployPath\Control\CustomSettings.ini 577 | Move-Item $MdtDeployPath\Control\CustomSettings-backup.ini $MdtDeployPath\Control\CustomSettings.ini 578 | Exit 579 | } 580 | } 581 | 582 | else { 583 | & $VBoxLoc\VBoxManage createvm --name $VmName --ostype "Windows10_64" --register 584 | } 585 | 586 | Write-Log -Type Info -Evt "(TSID:$Id) Configuring VM Processor Count" 587 | Write-Log -Type Info -Evt "(TSID:$Id) Configuring VM Static Memory" 588 | Write-Log -Type Info -Evt "(TSID:$Id) Configuring VM to boot from $BootMedia" 589 | 590 | If ($Vbox -eq $false) 591 | { 592 | ## Configure the VM with 2 vCPUs, static RAM and disable checkpoints. 593 | ## Set the boot CD to the configured ISO. 594 | ## Start the VM 595 | Set-VM $VmName -ProcessorCount 2 -StaticMemory -AutomaticCheckpointsEnabled $false -ComputerName $VmHost 596 | 597 | try { 598 | Set-VMDvdDrive -VMName $VmName -ControllerNumber 1 -ControllerLocation 0 -Path $BootMedia -ComputerName $VmHost -ErrorAction Stop 599 | } 600 | 601 | catch { 602 | Write-Log -Type Err -Evt "(TSID:$Id) $_" 603 | 604 | ## If -Remote switch is set, remove the VMs VHD's from the remote server. 605 | ## If switch is not set, the VM's VHDs are removed from the local computer. 606 | If ($Remote) 607 | { 608 | $VmBye = Get-VM -Name $VmName -ComputerName $VmHost 609 | $Disks = Get-VHD -VMId $VmBye.Id -ComputerName $VmHost 610 | Write-Log -Type Info -Evt "(TSID:$Id) Deleting $VmName on $VmHost" 611 | Invoke-Command {Remove-Item $using:disks.path -Force} -ComputerName $VmBye.ComputerName 612 | Start-Sleep -Seconds 5 613 | } 614 | 615 | else { 616 | $VmLocal = Get-VM -Name $VmName -ComputerName $VmHost 617 | Write-Log -Type Info -Evt "(TSID:$Id) Deleting $VmName on $VmHost" 618 | Remove-Item $VmLocal.HardDrives.Path -Force 619 | } 620 | 621 | ## Remove Hyper-V VM from remote or local 622 | Remove-VM $VmName -ComputerName $VmHost -Force 623 | 624 | ## Restore CustomSettings.ini from the backup. 625 | Write-Log -Type Info -Evt "(TSID:$Id) Restoring MDT CustomSettings.ini from backup" 626 | Remove-Item $MdtDeployPath\Control\CustomSettings.ini 627 | Move-Item $MdtDeployPath\Control\CustomSettings-backup.ini $MdtDeployPath\Control\CustomSettings.ini 628 | Exit 629 | } 630 | 631 | Write-Log -Type Info -Evt "(TSID:$Id) Starting $VmName on $VmHost" 632 | Start-VM $VmName -ComputerName $VmHost 633 | } 634 | 635 | else { 636 | & $VBoxLoc\VBoxManage modifyvm $VmName --cpus 2 637 | & $VBoxLoc\VBoxManage modifyvm $VmName --memory 2048 --vram 128 638 | & $VBoxLoc\VBoxManage modifyvm $VmName --nic1 nat 639 | & $VBoxLoc\VBoxManage createhd --filename $VhdPath\$VmName.vdi --size 130048 --format VDI 640 | & $VBoxLoc\VBoxManage storagectl $VmName --name "SATA Controller" --add sata --controller IntelAhci 641 | & $VBoxLoc\VBoxManage storageattach $VmName --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium $VhdPath\$VmName.vdi 642 | & $VBoxLoc\VBoxManage storagectl $VmName --name "IDE Controller" --add ide --controller PIIX4 643 | & $VBoxLoc\VBoxManage storageattach $VmName --storagectl "IDE Controller" --port 1 --device 0 --type dvddrive --medium $BootMedia 644 | & $VBoxLoc\VBoxManage modifyvm $VmName --boot1 dvd --boot2 disk --boot3 none --boot4 none 645 | Write-Log -Type Info -Evt "(TSID:$Id) Waiting for $VmName to shutdown" 646 | & $VBoxLoc\VBoxHeadless --startvm $VmName 647 | } 648 | 649 | If ($Vbox -eq $false) 650 | { 651 | ## Wait until the VM is turned off. 652 | Write-Log -Type Info -Evt "(TSID:$Id) Waiting for $VmName to shutdown" 653 | While ((Get-VM -Name $VmName -ComputerName $VmHost).state -ne 'Off') {Start-Sleep -Seconds 5} 654 | 655 | ## Change VM config to remove boot ISO. 656 | Set-VMDvdDrive -VMName $VmName -ControllerNumber 1 -ControllerLocation 0 -Path $null -ComputerName $VmHost 657 | Set-VM -VMName $VmName -AutomaticStartAction Nothing -AutomaticStopAction Shutdown -ComputerName $VmHost 658 | } 659 | 660 | ## Restore CustomSettings.ini from the backup. 661 | Write-Log -Type Info -Evt "(TSID:$Id) Restoring MDT CustomSettings.ini from backup" 662 | Remove-Item $MdtDeployPath\Control\CustomSettings.ini 663 | Move-Item $MdtDeployPath\Control\CustomSettings-backup.ini $MdtDeployPath\Control\CustomSettings.ini 664 | Write-Log -Type Info -Evt "End of Task Sequence ID: $Id" 665 | 666 | ## Increase count for progress bar 667 | $i = $i+1 668 | $Succi = $Succi+1 669 | 670 | If ($ProgCheck) 671 | { 672 | Notify 673 | } 674 | } 675 | ## 676 | ## End of the deploy process for TS's 677 | ## 678 | 679 | Write-Log -Type Info -Evt "Process finished" 680 | 681 | If ($Null -ne $LogHistory) 682 | { 683 | ## Cleanup logs. 684 | Write-Log -Type Info -Evt "Deleting logs older than: $LogHistory days" 685 | Get-ChildItem -Path "$LogPath\Image-Factory-Deploy_*" -File | Where-Object CreationTime -lt (Get-Date).AddDays(-$LogHistory) | Remove-Item -Recurse 686 | } 687 | 688 | If ($ProgCheck -eq $false) 689 | { 690 | Notify 691 | } 692 | } 693 | ## End -------------------------------------------------------------------------------- /Image-Factory.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 23.09.01 4 | 5 | .GUID 251ae35c-cc4e-417c-970c-848b221477fa 6 | 7 | .AUTHOR Mike Galvin Contact: digressive@outlook.com 8 | 9 | .COMPANYNAME Mike Galvin 10 | 11 | .COPYRIGHT (C) Mike Galvin. All rights reserved. 12 | 13 | .TAGS Microsoft Deployment Toolkit MDT Hyper-V Windows OSD 14 | 15 | .LICENSEURI https://github.com/Digressive/Image-Factory?tab=MIT-1-ov-file 16 | 17 | .PROJECTURI https://gal.vin/utils/image-factory-utility/ 18 | 19 | .ICONURI 20 | 21 | .EXTERNALMODULEDEPENDENCIES 22 | 23 | .REQUIREDSCRIPTS 24 | 25 | .EXTERNALSCRIPTDEPENDENCIES 26 | 27 | .RELEASENOTES 28 | 29 | #> 30 | 31 | <# 32 | .SYNOPSIS 33 | Image Factory Utility - Automate creation of WIM files. 34 | 35 | .DESCRIPTION 36 | Will create disposable virtual machines to generate WIM files from Microsoft Deployment Toolkit Task Sequences. 37 | Run with -help or no arguments for usage. 38 | #> 39 | 40 | ## Set up command line switches. 41 | [CmdletBinding()] 42 | Param( 43 | [alias("Build")] 44 | $MdtBuildPathUsr, 45 | [alias("Deploy")] 46 | $MdtDeployPathUsr, 47 | [alias("TS")] 48 | $TsId, 49 | [alias("VH")] 50 | $VmHost, 51 | [alias("VHD")] 52 | $VhdPathUsr, 53 | [alias("Boot")] 54 | $BootMedia, 55 | [alias("VNic")] 56 | $VmNic, 57 | [alias("L")] 58 | $LogPathUsr, 59 | [alias("LogRotate")] 60 | $LogHistory, 61 | [alias("Subject")] 62 | $MailSubject, 63 | [alias("SendTo")] 64 | $MailTo, 65 | [alias("From")] 66 | $MailFrom, 67 | [alias("Smtp")] 68 | $SmtpServer, 69 | [alias("Port")] 70 | $SmtpPort, 71 | [alias("User")] 72 | $SmtpUser, 73 | [alias("Pwd")] 74 | [ValidateScript({Test-Path -Path $_ -PathType Leaf})] 75 | $SmtpPwd, 76 | [switch]$UseSsl, 77 | [switch]$Compat, 78 | [switch]$Remote, 79 | [switch]$VBox, 80 | [switch]$Help, 81 | [switch]$ProgCheck, 82 | [switch]$NoBanner) 83 | 84 | If ($NoBanner -eq $False) 85 | { 86 | Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " 87 | .___ ___________ __ ____ ___ __ .__.__ .__ __ 88 | | | _____ ____ \_ _____/____ _____/ |_ ___________ ___.__. | | \_/ |_|__| | |__|/ |_ ___.__. 89 | | |/ \ / ___\ | __) \__ \ _/ ___\ __\/ _ \_ __ < | | | | /\ __\ | | | \ __< | | 90 | | | Y Y \/ /_/ > | \ / __ \\ \___| | ( <_> ) | \/\___ | | | / | | | | |_| || | \___ | 91 | |___|__|_| /\___ / \___ / (____ /\___ >__| \____/|__| / ____| |______/ |__| |__|____/__||__| / ____| 92 | \//_____/ \/ \/ \/ \/ \/ 93 | Mike Galvin Version 23.09.01 94 | https://gal.vin See -help for usage 95 | Donate: https://www.paypal.me/digressive 96 | " 97 | } 98 | 99 | If ($PSBoundParameters.Values.Count -eq 0 -or $Help) 100 | { 101 | Write-Host -Object " Usage: 102 | From a terminal run: [path\]Image-Factory.ps1 -Build [path\] -Deploy [path\] -Boot [path\]LiteTouchPE_x64.iso 103 | -Vnic [virtual NIC name] -Ts W11-21H2,W10-21H2 104 | This will use Hyper-V VMs on the local machine to build wim files from the task sequences W11-21H2 and W10-21H2. 105 | The wim files will be imported to the deployment share specified. 106 | 107 | Use -VH [hostname] to specify a remote Hyper-V server. 108 | Please note that -Boot and -VHD paths will be local to the remote server. 109 | 110 | Use -VHD [path\] to configure where to store the VM's VHD, if not the default. 111 | The -Compat should only be used if a remote Hyper-V server is running Windows Server 2012 R2 112 | and this script is running on a later version of Windows. 113 | 114 | Use -Remote when the Hyper-V server is a remote computer. 115 | Use -VBox if using Virtual Box instead of Hyper-V as the VM platform. 116 | Use -ProgCheck to send notifications (email or webhook) after each Task Sequence is processed. 117 | 118 | To output a log: -L [path\]. 119 | To remove logs produced by the utility older than X days: -LogRotate [number]. 120 | Run with no ASCII banner: -NoBanner 121 | 122 | To use the 'email log' function: 123 | Specify the subject line with -Subject ""'[subject line]'"" If you leave this blank a default subject will be used 124 | Make sure to encapsulate it with double & single quotes as per the example for Powershell to read it correctly. 125 | 126 | Specify the 'to' address with -SendTo [example@contoso.com] 127 | For multiple address, separate with a comma. 128 | 129 | Specify the 'from' address with -From [example@contoso.com] 130 | Specify the SMTP server with -Smtp [smtp server name] 131 | 132 | Specify the port to use with the SMTP server with -Port [port number]. 133 | If none is specified then the default of 25 will be used. 134 | 135 | Specify the user to access SMTP with -User [example@contoso.com] 136 | Specify the password file to use with -Pwd [path\]ps-script-pwd.txt. 137 | Use SSL for SMTP server connection with -UseSsl. 138 | 139 | To generate an encrypted password file run the following commands 140 | on the computer and the user that will run the script: 141 | " 142 | Write-Host -Object ' $creds = Get-Credential 143 | $creds.Password | ConvertFrom-SecureString | Set-Content [path\]ps-script-pwd.txt' 144 | } 145 | 146 | else { 147 | ## If logging is configured, start logging. 148 | ## If the log file already exists, clear it. 149 | If ($LogPathUsr) 150 | { 151 | ## Clean User entered string 152 | $LogPath = $LogPathUsr.trimend('\') 153 | 154 | ## Make sure the log directory exists. 155 | If ((Test-Path -Path $LogPath) -eq $False) 156 | { 157 | New-Item $LogPath -ItemType Directory -Force | Out-Null 158 | } 159 | 160 | $LogFile = ("Image-Factory_{0:yyyy-MM-dd_HH-mm-ss}.log" -f (Get-Date)) 161 | $Log = "$LogPath\$LogFile" 162 | 163 | If (Test-Path -Path $Log) 164 | { 165 | Clear-Content -Path $Log 166 | } 167 | } 168 | 169 | ## Function to get date in specific format. 170 | Function Get-DateFormat 171 | { 172 | Get-Date -Format "yyyy-MM-dd HH:mm:ss" 173 | } 174 | 175 | ## Function for logging. 176 | Function Write-Log($Type, $Evt) 177 | { 178 | If ($Type -eq "Info") 179 | { 180 | If ($LogPathUsr) 181 | { 182 | Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [INFO] $Evt" 183 | } 184 | 185 | Write-Host -Object " $(Get-DateFormat) [INFO] $Evt" 186 | } 187 | 188 | If ($Type -eq "Succ") 189 | { 190 | If ($LogPathUsr) 191 | { 192 | Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [SUCCESS] $Evt" 193 | } 194 | 195 | Write-Host -ForegroundColor Green -Object " $(Get-DateFormat) [SUCCESS] $Evt" 196 | } 197 | 198 | If ($Type -eq "Err") 199 | { 200 | If ($LogPathUsr) 201 | { 202 | Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [ERROR] $Evt" 203 | } 204 | 205 | Write-Host -ForegroundColor Red -BackgroundColor Black -Object " $(Get-DateFormat) [ERROR] $Evt" 206 | } 207 | 208 | If ($Type -eq "Conf") 209 | { 210 | If ($LogPathUsr) 211 | { 212 | Add-Content -Path $Log -Encoding ASCII -Value "$Evt" 213 | } 214 | 215 | Write-Host -ForegroundColor Cyan -Object " $Evt" 216 | } 217 | } 218 | 219 | ## Function for Notifications 220 | Function Notify() 221 | { 222 | ## This whole block is for e-mail, if it is configured. 223 | If ($SmtpServer) 224 | { 225 | If (Test-Path -Path $Log) 226 | { 227 | ## Default e-mail subject if none is configured. 228 | If ($Null -eq $MailSubject) 229 | { 230 | $MailSubject = "Image Factory Utility Log" 231 | } 232 | 233 | ## Default Smtp Port if none is configured. 234 | If ($Null -eq $SmtpPort) 235 | { 236 | $SmtpPort = "25" 237 | } 238 | 239 | ## Setting the contents of the log to be the e-mail body. 240 | $MailBody = Get-Content -Path $Log | Out-String 241 | 242 | ForEach ($MailAddress in $MailTo) 243 | { 244 | ## If an smtp password is configured, get the username and password together for authentication. 245 | ## If an smtp password is not provided then send the e-mail without authentication and obviously no SSL. 246 | If ($SmtpPwd) 247 | { 248 | $SmtpPwdEncrypt = Get-Content $SmtpPwd | ConvertTo-SecureString 249 | $SmtpCreds = New-Object System.Management.Automation.PSCredential -ArgumentList ($SmtpUser, $SmtpPwdEncrypt) 250 | 251 | ## If -ssl switch is used, send the email with SSL. 252 | ## If it isn't then don't use SSL, but still authenticate with the credentials. 253 | If ($UseSsl) 254 | { 255 | Send-MailMessage -To $MailAddress -From $MailFrom -Subject "$MailSubject $Succi/$($TsId.count) TSs Successful" -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpPort -UseSsl -Credential $SmtpCreds 256 | } 257 | 258 | else { 259 | Send-MailMessage -To $MailAddress -From $MailFrom -Subject "$MailSubject $Succi/$($TsId.count) TSs Successful" -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpPort -Credential $SmtpCreds 260 | } 261 | } 262 | 263 | else { 264 | Send-MailMessage -To $MailAddress -From $MailFrom -Subject "$MailSubject $Succi/$($TsId.count) TSs Successful" -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpPort 265 | } 266 | } 267 | } 268 | else { 269 | Write-Host -ForegroundColor Red -BackgroundColor Black -Object " There's no log file to email." 270 | } 271 | } 272 | ## End of Email block 273 | } 274 | 275 | ## Function for Update Check 276 | Function UpdateCheck() 277 | { 278 | $ScriptVersion = "23.09.01" 279 | $RawSource = "https://raw.githubusercontent.com/Digressive/Image-Factory/master/Image-Factory.ps1" 280 | 281 | try { 282 | $SourceCheck = Invoke-RestMethod -uri "$RawSource" 283 | $VerCheck = $SourceCheck -split '\n' | Select-String -Pattern ".VERSION $ScriptVersion" -SimpleMatch -CaseSensitive -Quiet 284 | 285 | If ($VerCheck -ne $True) 286 | { 287 | Write-Log -Type Conf -Evt "*** There is an update available. ***" 288 | } 289 | } 290 | 291 | catch { 292 | } 293 | } 294 | 295 | ## Config checks for conflicting options, needless options. 296 | If ($Null -eq $MdtBuildPathUsr) 297 | { 298 | Write-Log -Type Err -Evt "The Build share is not specified." 299 | Exit 300 | } 301 | 302 | If ($Null -eq $MdtDeployPathUsr) 303 | { 304 | Write-Log -Type Err -Evt "The Deployment share is not specified." 305 | Exit 306 | } 307 | 308 | If ($Null -eq $TsId) 309 | { 310 | Write-Log -Type Err -Evt "No Task Sequence IDs are specified." 311 | Exit 312 | } 313 | 314 | If ($Null -eq $BootMedia) 315 | { 316 | Write-Log -Type Err -Evt "The boot media is not specified." 317 | Exit 318 | } 319 | 320 | If ($Null -eq $VmNic) 321 | { 322 | If ($Vbox -eq $false) 323 | { 324 | Write-Log -Type Err -Evt "The virtual NIC is not specified." 325 | Exit 326 | } 327 | } 328 | 329 | else { 330 | If ($Vbox -eq $true) 331 | { 332 | Write-Log -Type Info -Evt "This setting is ignored with Virtual Box." 333 | Exit 334 | } 335 | } 336 | 337 | If ($Null -ne $Vmhost -AND $Vbox -eq $True) 338 | { 339 | Write-Log -Type Err -Evt "The VM host does not need to be configured with the -VBox switch." 340 | Exit 341 | } 342 | 343 | ## If not configured set VmHost to local 344 | If ($Null -eq $VmHost) 345 | { 346 | If ($Remote) 347 | { 348 | If ($Vbox) 349 | { 350 | Write-Log -Type Err -Evt "The -Remote switch has no effect with Virtual Box, Virtual Box must be installed locally." 351 | Exit 352 | } 353 | 354 | Write-Log -Type Err -Evt "You must specify the remote VM host when the -Remote switch is set." 355 | Exit 356 | } 357 | 358 | $VmHost = $Env:ComputerName 359 | 360 | If ($Vbox) 361 | { 362 | $VBoxLoc = "C:\Program Files\Oracle\VirtualBox" 363 | 364 | If ((Test-Path -Path $VBoxLoc) -eq $False) 365 | { 366 | Write-Log -Type Err -Evt "Virtual Box is not installed on this local machine." 367 | Exit 368 | } 369 | } 370 | 371 | else { 372 | ## Test for Hyper-V feature installed on local machine. 373 | try { 374 | If ($OSV -eq "6.3.9600") 375 | { 376 | Get-Service vmms -ErrorAction Stop | Out-Null 377 | } 378 | 379 | else { 380 | Get-Service vmcompute -ErrorAction Stop | Out-Null 381 | } 382 | } 383 | 384 | catch { 385 | Write-Log -Type Err -Evt "Hyper-V is not installed on this local machine." 386 | Exit 387 | } 388 | } 389 | } 390 | 391 | else { 392 | If ($Remote -eq $False) 393 | { 394 | Write-Log -Type Err -Evt "You must use the -Remote switch when specifying a remote VM host." 395 | Exit 396 | } 397 | } 398 | 399 | ## If not configured set VhdPath to the default 400 | If ($Null -eq $VhdPathUsr) 401 | { 402 | If ($Vbox -eq $false) 403 | { 404 | $VhdPathUsr = Get-VMHost -Computer $VmHost | Select-Object VirtualHardDiskPath -ExpandProperty VirtualHardDiskPath 405 | } 406 | 407 | else { 408 | Write-Log -Type Err -Evt "You must configure a VHD storage path when using Virtual Box." 409 | Exit 410 | } 411 | } 412 | 413 | If ($Compat -AND $Vbox) 414 | { 415 | Write-Log -Type Err -Evt "The -Compat switch has no effect with the -VBox switch." 416 | Exit 417 | } 418 | 419 | If ($Null -eq $LogPathUsr -And $SmtpServer) 420 | { 421 | Write-Log -Type Err -Evt "You must specify -L [path\] to use the email log function." 422 | Exit 423 | } 424 | 425 | ## getting Windows Version info 426 | $OSVMaj = [environment]::OSVersion.Version | Select-Object -expand major 427 | $OSVMin = [environment]::OSVersion.Version | Select-Object -expand minor 428 | $OSVBui = [environment]::OSVersion.Version | Select-Object -expand build 429 | $OSV = "$OSVMaj" + "." + "$OSVMin" + "." + "$OSVBui" 430 | 431 | ## 432 | ## Display the current config and log if configured. 433 | ## 434 | Write-Log -Type Conf -Evt "--- Running with the following config ---" 435 | Write-Log -Type Conf -Evt "Utility Version: 23.09.01" 436 | UpdateCheck ## Run Update checker function 437 | Write-Log -Type Conf -Evt "Hostname: $Env:ComputerName." 438 | Write-Log -Type Conf -Evt "Windows Version: $OSV." 439 | 440 | If ($MdtBuildPathUsr) 441 | { 442 | Write-Log -Type Conf -Evt "Build share: $MdtBuildPathUsr." 443 | } 444 | 445 | If ($MdtDeployPathUsr) 446 | { 447 | Write-Log -Type Conf -Evt "Deploy share: $MdtDeployPathUsr." 448 | } 449 | 450 | If ($TsId) 451 | { 452 | Write-Log -Type Conf -Evt "No. of TS ID's: $($TsId.count)." 453 | Write-Log -Type Conf -Evt "TS ID's:" 454 | ForEach ($Id in $TsId) 455 | { 456 | Write-Log -Type Conf -Evt "$Id" 457 | } 458 | } 459 | 460 | If ($VmHost) 461 | { 462 | Write-Log -Type Conf -Evt "VM Host: $VmHost." 463 | } 464 | 465 | If ($VhdPathUsr) 466 | { 467 | Write-Log -Type Conf -Evt "VHD path: $VhdPathUsr." 468 | } 469 | 470 | If ($BootMedia) 471 | { 472 | Write-Log -Type Conf -Evt "Boot media path: $BootMedia." 473 | } 474 | 475 | If ($VmNic) 476 | { 477 | Write-Log -Type Conf -Evt "Virtual NIC name: $VmNic." 478 | } 479 | 480 | If ($LogPathUsr) 481 | { 482 | Write-Log -Type Conf -Evt "Logs directory: $LogPath." 483 | } 484 | 485 | If ($Null -ne $LogHistory) 486 | { 487 | Write-Log -Type Conf -Evt "Logs to keep: $LogHistory days." 488 | } 489 | 490 | If ($MailTo) 491 | { 492 | Write-Log -Type Conf -Evt "E-mail log to: $MailTo." 493 | } 494 | 495 | If ($MailFrom) 496 | { 497 | Write-Log -Type Conf -Evt "E-mail log from: $MailFrom." 498 | } 499 | 500 | If ($MailSubject) 501 | { 502 | Write-Log -Type Conf -Evt "E-mail subject: $MailSubject." 503 | } 504 | 505 | If ($SmtpServer) 506 | { 507 | Write-Log -Type Conf -Evt "SMTP server: Configured." 508 | } 509 | 510 | If ($SmtpUser) 511 | { 512 | Write-Log -Type Conf -Evt "SMTP Auth: Configured." 513 | } 514 | 515 | If ($VBox) 516 | { 517 | Write-Log -Type Conf -Evt "-VBox switch: $VBox." 518 | } 519 | 520 | If ($Compat) 521 | { 522 | Write-Log -Type Conf -Evt "-Compat switch: $Compat." 523 | } 524 | 525 | If ($Remote) 526 | { 527 | Write-Log -Type Conf -Evt "-Remote switch: $Remote." 528 | } 529 | Write-Log -Type Conf -Evt "---" 530 | Write-Log -Type Info -Evt "Process started" 531 | ## 532 | ## Display current config ends here. 533 | ## 534 | 535 | ##Clean the user paths 536 | $VhdPath = $VhdPathUsr.trimend('\') 537 | $MdtBuildPath = $MdtBuildPathUsr.trimend('\') 538 | $MdtDeployPath = $MdtDeployPathUsr.trimend('\') 539 | 540 | ## If the -Compat switch is used, load the older Hyper-V PS module. 541 | If ($Vbox -eq $false) 542 | { 543 | If ($Compat) 544 | { 545 | Write-Log -Type Info -Evt "Importing Hyper-V 1.1 PowerShell Module" 546 | Import-Module $env:windir\System32\WindowsPowerShell\v1.0\Modules\Hyper-V\1.1\Hyper-V.psd1 547 | } 548 | } 549 | 550 | ## If the -VBox switch is used, set the location of the default Virtual Box installation. 551 | else { 552 | $VBoxLoc = "C:\Program Files\Oracle\VirtualBox" 553 | } 554 | 555 | ## Import the Deployment Toolkit PowerShell module. 556 | Write-Log -Type Info -Evt "Importing MDT PowerShell Module" 557 | Import-Module "$env:programfiles\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" 558 | 559 | ## Create a new PSDrive to the configured MDT deploy path. 560 | Write-Log -Type Info -Evt "Creating PSDrive to $MdtDeployPath" 561 | New-PSDrive -Name "ImgFacDeploy" -PSProvider MDTProvider -Root $MdtDeployPath | Out-Null 562 | 563 | ## For Success/Fail stats 564 | $Succi = 0 565 | 566 | ## For Progress bar 567 | $i = 0 568 | 569 | ## 570 | ## For each of the Task Sequence ID's configured, run the build process. 571 | ## 572 | ForEach ($Id in $TsId) 573 | { 574 | ## Progress Bar based on progress through the TS ID's 575 | Write-Progress -Id 0 -Activity "Processing" -Status "Current TSID: $Id" -PercentComplete ($i/$TsId.count*100) 576 | 577 | ## Test to see if the build environment is dirty from another run, if it is exit the script. 578 | If (Test-Path -Path $MdtBuildPath\Control\CustomSettings-backup.ini) 579 | { 580 | Write-Log -Type Err -Evt "CustomSettings-backup.ini already exists." 581 | Write-Log -Type Err -Evt "The build environment is dirty." 582 | Write-Log -Type Err -Evt "Did the script finish successfully last time it was run?" 583 | Exit 584 | } 585 | 586 | Write-Log -Type Info -Evt "(TSID:$Id) Start of Task Sequence ID: $Id" 587 | Write-Log -Type Info -Evt "(TSID:$Id) Backing up current MDT CustomSettings.ini" 588 | 589 | ## Backup the existing CustomSettings.ini. 590 | Copy-Item $MdtBuildPath\Control\CustomSettings.ini $MdtBuildPath\Control\CustomSettings-backup.ini 591 | Start-Sleep -Seconds 5 592 | 593 | Write-Log -Type Info -Evt "(TSID:$Id) Setting MDT CustomSettings.ini for Task Sequence" 594 | 595 | ## Setup MDT CustomSettings.ini for auto deploy. 596 | Add-Content $MdtBuildPath\Control\CustomSettings.ini "" 597 | Add-Content $MdtBuildPath\Control\CustomSettings.ini "" 598 | Add-Content $MdtBuildPath\Control\CustomSettings.ini "TaskSequenceID=$Id" 599 | Add-Content $MdtBuildPath\Control\CustomSettings.ini "SkipTaskSequence=YES" 600 | Add-Content $MdtBuildPath\Control\CustomSettings.ini "SkipComputerName=YES" 601 | 602 | ## Set the VM name as build + the date and time. 603 | $VmName = ("$Id`_{0:yyyy-MM-dd_HH-mm-ss}" -f (Get-Date)) 604 | 605 | Write-Log -Type Info -Evt "(TSID:$Id) Creating VM: $VmName on $VmHost" 606 | Write-Log -Type Info -Evt "(TSID:$Id) Adding VHD: $VhdPath\$VmName.vhdx" 607 | 608 | If ($VmNic) 609 | { 610 | Write-Log -Type Info -Evt "(TSID:$Id) Adding Virtual NIC: $VmNic" 611 | } 612 | 613 | If ($Vbox -eq $false) 614 | { 615 | ## Create the VM with 4GB Dynamic RAM, Gen 1, 127GB VHD, and add the configured vNIC. 616 | try { 617 | New-VM -name $VmName -MemoryStartupBytes 4096MB -BootDevice CD -Generation 1 -NewVHDPath $VhdPath\$VmName.vhdx -NewVHDSizeBytes 130048MB -SwitchName $VmNic -ComputerName $VmHost -ErrorAction Stop | Out-Null 618 | } 619 | 620 | catch { 621 | Write-Log -Type Err -Evt "(TSID:$Id) $_" 622 | 623 | ## Restore CustomSettings.ini from the backup. 624 | Write-Log -Type Info -Evt "(TSID:$Id) Restoring MDT CustomSettings.ini from backup" 625 | Remove-Item $MdtBuildPath\Control\CustomSettings.ini 626 | Move-Item $MdtBuildPath\Control\CustomSettings-backup.ini $MdtBuildPath\Control\CustomSettings.ini 627 | Exit 628 | } 629 | } 630 | 631 | else { 632 | & $VBoxLoc\VBoxManage createvm --name $VmName --ostype "Windows10_64" --register 633 | } 634 | 635 | Write-Log -Type Info -Evt "(TSID:$Id) Configuring VM Processor Count" 636 | Write-Log -Type Info -Evt "(TSID:$Id) Configuring VM Static Memory" 637 | Write-Log -Type Info -Evt "(TSID:$Id) Configuring VM to boot from $BootMedia" 638 | 639 | If ($Vbox -eq $false) 640 | { 641 | ## Configure the VM with 2 vCPUs, static RAM and disable checkpoints. 642 | ## Set the boot CD to the configured ISO. 643 | ## Start the VM 644 | Set-VM $VmName -ProcessorCount 2 -StaticMemory -AutomaticCheckpointsEnabled $false -ComputerName $VmHost 645 | 646 | try { 647 | Set-VMDvdDrive -VMName $VmName -ControllerNumber 1 -ControllerLocation 0 -Path $BootMedia -ComputerName $VmHost -ErrorAction Stop 648 | } 649 | 650 | catch { 651 | Write-Log -Type Err -Evt "(TSID:$Id) $_" 652 | 653 | ## If -Remote switch is set, remove the VMs VHD's from the remote server. 654 | ## If switch is not set, the VM's VHDs are removed from the local computer. 655 | If ($Remote) 656 | { 657 | $VmBye = Get-VM -Name $VmName -ComputerName $VmHost 658 | $Disks = Get-VHD -VMId $VmBye.Id -ComputerName $VmHost 659 | Write-Log -Type Info -Evt "(TSID:$Id) Deleting $VmName on $VmHost" 660 | Invoke-Command {Remove-Item $using:disks.path -Force} -ComputerName $VmBye.ComputerName 661 | Start-Sleep -Seconds 5 662 | } 663 | 664 | else { 665 | $VmLocal = Get-VM -Name $VmName -ComputerName $VmHost 666 | Write-Log -Type Info -Evt "(TSID:$Id) Deleting $VmName on $VmHost" 667 | Remove-Item $VmLocal.HardDrives.Path -Force 668 | } 669 | 670 | ## Remove Hyper-V VM from remote or local 671 | Remove-VM $VmName -ComputerName $VmHost -Force 672 | 673 | ## Restore CustomSettings.ini from the backup. 674 | Write-Log -Type Info -Evt "(TSID:$Id) Restoring MDT CustomSettings.ini from backup" 675 | Remove-Item $MdtBuildPath\Control\CustomSettings.ini 676 | Move-Item $MdtBuildPath\Control\CustomSettings-backup.ini $MdtBuildPath\Control\CustomSettings.ini 677 | Exit 678 | } 679 | 680 | Write-Log -Type Info -Evt "(TSID:$Id) Starting $VmName on $VmHost" 681 | Start-VM $VmName -ComputerName $VmHost 682 | } 683 | 684 | else { 685 | & $VBoxLoc\VBoxManage modifyvm $VmName --cpus 2 686 | & $VBoxLoc\VBoxManage modifyvm $VmName --memory 2048 --vram 128 687 | & $VBoxLoc\VBoxManage modifyvm $VmName --nic1 nat 688 | & $VBoxLoc\VBoxManage createhd --filename $VhdPath\$VmName.vdi --size 130048 --format VDI 689 | & $VBoxLoc\VBoxManage storagectl $VmName --name "SATA Controller" --add sata --controller IntelAhci 690 | & $VBoxLoc\VBoxManage storageattach $VmName --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium $VhdPath\$VmName.vdi 691 | & $VBoxLoc\VBoxManage storagectl $VmName --name "IDE Controller" --add ide --controller PIIX4 692 | & $VBoxLoc\VBoxManage storageattach $VmName --storagectl "IDE Controller" --port 1 --device 0 --type dvddrive --medium $BootMedia 693 | & $VBoxLoc\VBoxManage modifyvm $VmName --boot1 dvd --boot2 disk --boot3 none --boot4 none 694 | Write-Log -Type Info -Evt "(TSID:$Id) Waiting for $VmName to shutdown" 695 | & $VBoxLoc\VBoxHeadless --startvm $VmName 696 | } 697 | 698 | If ($Vbox -eq $false) 699 | { 700 | ## Wait until the VM is turned off. 701 | Write-Log -Type Info -Evt "(TSID:$Id) Waiting for $VmName to shutdown" 702 | While ((Get-VM -Name $VmName -ComputerName $VmHost).state -ne 'Off') {Start-Sleep -Seconds 5} 703 | } 704 | 705 | If ($Vbox -eq $false) 706 | { 707 | ## If -Remote switch is set, remove the VMs VHD's from the remote server. 708 | ## If switch is not set, the VM's VHDs are removed from the local computer. 709 | If ($Remote) 710 | { 711 | $VmBye = Get-VM -Name $VmName -ComputerName $VmHost 712 | $Disks = Get-VHD -VMId $VmBye.Id -ComputerName $VmHost 713 | Write-Log -Type Info -Evt "(TSID:$Id) Deleting $VmName on $VmHost" 714 | Invoke-Command {Remove-Item $using:disks.path -Force} -ComputerName $VmBye.ComputerName 715 | Start-Sleep -Seconds 5 716 | } 717 | 718 | else { 719 | $VmLocal = Get-VM -Name $VmName -ComputerName $VmHost 720 | Write-Log -Type Info -Evt "(TSID:$Id) Deleting $VmName on $VmHost" 721 | Remove-Item $VmLocal.HardDrives.Path -Force 722 | } 723 | } 724 | 725 | If ($Vbox -eq $false) 726 | { 727 | Remove-VM $VmName -ComputerName $VmHost -Force 728 | } 729 | 730 | else { 731 | ## Remove VBox VM and Files here 732 | & $VBoxLoc\VBoxManage unregistervm $VmName --delete 733 | } 734 | 735 | ## Restore CustomSettings.ini from the backup. 736 | Write-Log -Type Info -Evt "(TSID:$Id) Restoring MDT CustomSettings.ini from backup" 737 | Remove-Item $MdtBuildPath\Control\CustomSettings.ini 738 | Move-Item $MdtBuildPath\Control\CustomSettings-backup.ini $MdtBuildPath\Control\CustomSettings.ini 739 | 740 | ## For each of the the WIM files in the captures folder of the build share, import 741 | ## them into the MDT Operating Systems folder. 742 | If (Test-Path -Path $MdtBuildPath\Captures\$Id`_*-*-*-*-*.wim -PathType Leaf) 743 | { 744 | $Wims = Get-ChildItem -Path $MdtBuildPath\Captures\$Id`_*-*-*-*-*.wim -File 745 | 746 | ForEach ($File in $Wims) 747 | { 748 | Write-Log -Type Info -Evt "(TSID:$Id) Importing WIM File: $File" 749 | Import-MDTOperatingSystem -Path "ImgFacDeploy:\Operating Systems" -SourceFile $File -DestinationFolder $File.Name | Out-Null 750 | Rename-Item -Path "ImgFacDeploy:\Operating Systems\$Id* in $Id`_*-*-*-*-*.wim $Id`_*-*-*-*-*.wim" -NewName ("$Id`_{0:yyyy-MM-dd_HH-mm-ss}" -f (Get-Date)) 751 | } 752 | 753 | ## Cleanup the WIM files in the captures folder of the build share. 754 | Write-Log -Type Info -Evt "(TSID:$Id) Removing captured WIM file" 755 | Remove-Item $MdtBuildPath\Captures\$Id`_*-*-*-*-*.wim 756 | } 757 | 758 | else { 759 | Write-Log -Type Err -Evt "(TSID:$Id) No wim file found." 760 | } 761 | 762 | Write-Log -Type Info -Evt "(TSID:$Id) End of Task Sequence ID: $Id" 763 | 764 | ## Increase count for progress bar 765 | $i = $i+1 766 | $Succi = $Succi+1 767 | 768 | If ($ProgCheck) 769 | { 770 | Notify 771 | } 772 | } 773 | ## 774 | ## End of the build and capture process for TS's 775 | ## 776 | 777 | Write-Log -Type Info -Evt "Process finished" 778 | 779 | If ($Null -ne $LogHistory) 780 | { 781 | ## Cleanup logs. 782 | Write-Log -Type Info -Evt "Deleting logs older than: $LogHistory days" 783 | Get-ChildItem -Path "$LogPath\Image-Factory_*" -File | Where-Object CreationTime -lt (Get-Date).AddDays(-$LogHistory) | Remove-Item -Recurse 784 | } 785 | 786 | If ($ProgCheck -eq $false) 787 | { 788 | Notify 789 | } 790 | } 791 | ## End --------------------------------------------------------------------------------