# createTaskScheduler.ps1
# What this script does:
# 1. Ask user for valid Domain Admin credentials
# 2. Connect to each servers on a list
# 3. Copy Script file from a UNC path onto local C:\Scripts directory(ies)
# 4. Add a scheduled task on each machine using the Domain Administrator credential
# Requires: Windows 2012 or higher
# Scheduled task variables
$computers='SERVER01','SERVER02','SERVER03'
$scriptFile='\\JUMPBOX\C$\Scripts\asyncMaintenance.ps1'
$description="Put Some Descriptive Words Here"
$taskName="Task by $env:username"
$repeatIntervalMinutes=60
# Obtain Domain Admin Credentials
function obtainDomainAdminCredentials{
# Legacy domain binding function
function isValidCred($u,$p){
# Get current domain using logged-on user's credentials
$domain = "LDAP://" + ([ADSI]"").distinguishedName
$domainCred = New-Object System.DirectoryServices.DirectoryEntry($domain,$u,$p)
if ($domainCred){return $True}else{return $False}
}
function isDomainAdmin($username){
if (!(get-module activedirectory)){Install-WindowsFeature RSAT-AD-PowerShell -Confirm:$false}
if((Get-ADUser $username -Properties MemberOf).MemberOf -match 'Domain Admins'){return $True;}else{return $false;}
}
# Create a domain context
function dotNetDomainBind{
Add-Type -AssemblyName System.DirectoryServices.AccountManagement;
Try {
$type = [System.DirectoryServices.AccountManagement.ContextType]::Domain;
$context = New-Object System.DirectoryServices.AccountManagement.PrincipalContext $type,$env:USERDOMAIN;
return $context;
} Catch {
If ($_.Exception.InnerException -like "*The server could not be contacted*") {
write-host "Could not contact a server for the specified domain $env:USERDOMAIN via DotNet Method.";
} Else {
write-host "Unknown errors occured while attempting to contact $env:USERDOMAIN via DotNet Method.";
}
return $false;
}
}
$attempt=0;
$maxAttempts=3;
$plainTextPassword='';
Do{
$attempt++;
$failureMessage = $null;
[string][ValidateNotNullOrEmpty()]$userName=Read-Host -Prompt "Please input a User ID";
if($userName -match '\\'){$username=$env:USERDOMAIN+'\'+$userName}
$password = Read-Host -Prompt "Please type in the password for user $userName" -AsSecureString;
$plainTextPassword=[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
# Test bind to this credential
try {
# Test bind
$context=dotNetDomainBind
if($context){
$validatedAccount = $context.ValidateCredentials($userName,$plainTextPassword)
}else{
$validatedAccount=isValidCred -u $userName -p $plainTextPassword
}
If ($validatedAccount) {
$isDomainAdmin=isDomainAdmin -username $userName;
if($isDomainAdmin){
$credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName,$password;
$validAccount=$true;
}else{
$failureMessage += "$attempt out of $maxAttempts`: $userName account is valid, but it is not a Domain Admin";
}
}else{
$failureMessage += "$attempt out of $maxAttempts`: username and/or password error";
}
}catch{
$failureMessage += "Unable to bind to $env:USERDOMAIN.";
}
# Depending on whether there are failures, proceed accordingly
if($failureMessage){
If ($attempt -lt $maxAttempts-1) {
$message = "$failureMessage`: Authentication error. Please Try again.";
Write-Warning $message;
} elseif ($attempt -eq $maxAttempts-1){
$message = "$failureMessage`: Last attempt.";
Write-Warning $message;
$credentials= $false;
}
}
} Until (($ValidAccount) -or ($Attempt -eq $MaxAttempts))
if($credentials){return $credentials;}else{return $false}
}
function setScheduledTasks{
param(
[string[]]$computernames,
[string]$scriptFile,
[string]$description,
[string]$repeatIntervalMinutes,
[string]$taskName,
[System.Management.Automation.PSCredential]$credential
)
function getUncServer($path){
$uncServer=$scriptFile | select-string -pattern "(?<=\\\\)(.*?)(?=\\)" | Select -ExpandProperty Matches | Select -ExpandProperty Value
$isValidPath=test-path $scriptfile -ErrorAction SilentlyContinue
if ($uncServer -and $isValidPath){
return $uncServer;
}else{
return $False;
}
}
$username=$credential.Username;
$plaintextPassword=$credential.GetNetworkCredential().password;
$uncServer=getUncServer $scriptFile
if ($credential){
foreach ($computer in $computernames){
write-host "Adding task on $computer..."
if($session.state -eq 'Opened'){remove-pssession $session}
do{
$session=if($credential){
try{
New-PSSession -ComputerName $computer -Credential $credential -ea Stop
}catch{
New-PSSession -ComputerName $computer -Credential $credential -SessionOption $(new-pssessionoption -IncludePortInSPN)
}
}else{
try{
New-PSSession -ComputerName $computer -ea Stop
}catch{
New-PSSession -ComputerName $computer -SessionOption $(new-pssessionoption -IncludePortInSPN)
}
}
} until ($session.state -match "Opened")
Invoke-Command -session $session -ScriptBlock{
param($scriptFile,$taskName,$description,$repeatMinutes,$user,$password,$uncServer)
$originalScriptPath=$scriptFile
$windowsVersion=[Environment]::OSVersion.Version
if($windowsVersion -gt [version]'6.1'){
$username="$env:USERDOMAIN`\$user"
$encryptedPassword=ConvertTo-SecureString $password -AsPlainText -Force
$adminCredential=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username,$encryptedPassword;
$destinationPath='C:\Scripts\'
$null=mkdir $destinationPath -force
$fileName=split-path $scriptfile -leaf
$sourceDirectory=split-path $scriptfile -parent
# Unrestrict this Domain Administrator from security prompts
Set-Executionpolicy -Scope CurrentUser -ExecutionPolicy UnRestricted -Force
try{
$unavailableDriveLetters=(Get-WmiObject -Class Win32_Volume).DriveLetter|sort
$availableDriveLetters=.{(65..90|%{[char]$_})|?{"$_`:" -notin $unavailableDriveLetters}}
[char]$firstAvailableDriveLetter=$availableDriveLetters[0]
$scriptFile=$destinationPath+$fileName
$null=New-PSDrive -Name $firstAvailableDriveLetter -PSProvider FileSystem -Root $sourceDirectory -Persist -credential $adminCredential
$command="Copy-Item '$firstAvailableDriveLetter`:\$fileName' '$destinationPath' -force"
$fileAlreadyAvailable=.{
try{
$x=(get-item $scriptFile -ea SilentlyContinue).LastWriteTime
$y=(get-item "$firstAvailableDriveLetter`:\$fileName" -ea SilentlyContinue).LastWriteTime
return $x -eq $y
}catch{
return $false
}
}
if($fileAlreadyAvailable){
write-host "$scriptFile is already available at the destination."
}else{
write-host $command
invoke-expression $command
}
Remove-PSDrive $firstAvailableDriveLetter
}catch{
write-warning $_
return $false
}
# Unblock file & Ensure that script exists
<# Overcome error caused by double hop issue:
Cannot find path '\\snapshots\FileServerClusters\Daily-VSS-Snapshot.ps1' because it does not exist.
+ CategoryInfo : ObjectNotFound: (\\snapshots\Fil...SS-Snapshot.ps1:String) [Unblock-File], ItemNotFoundException
+ FullyQualifiedErrorId : FileNotFound,Microsoft.PowerShell.Commands.UnblockFileCommand
+ PSComputerName : SHERVER007
1. Run scheduled task as: New-ScheduledTaskAction -Execute "Powershell.exe" -Argument "-ExecutionPolicy Bypass $scriptFile"
2. Unblock-File -Path $scriptFile
#>
if($uncServer -and $originalScriptPath -eq $scriptFile){
$unblockFile=Invoke-Command -computername $uncServer -Credential $adminCredential -ScriptBlock{
Unblock-File -Path $Args[0];Test-Path $Args[0] -ErrorAction SilentlyContinue} -Args $scriptFile
if ($unblockFile -eq $False) {
write-warning "Errors locating $scriptFile... Aborting execution on $env:computername"
}
}else{
$unblockFile=.{Unblock-File -Path $scriptFile;Test-Path $scriptFile -ErrorAction SilentlyContinue}
if ($unblockFile -eq $False) {
write-warning "Errors locating $scriptFile... Aborting execution on $env:computername"
}
}
$settingsCommand = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -ExecutionTimeLimit 0
$callPowerShell = New-ScheduledTaskAction -Execute "Powershell.exe" -Argument "-ExecutionPolicy Bypass $scriptFile"
$taskTrigger = New-ScheduledTaskTrigger `
-Once `
-At (Get-Date) `
-RepetitionInterval (New-TimeSpan -Minutes $repeatMinutes)
# Unregister the Scheduled task if it already exists
Get-ScheduledTask $taskName -ErrorAction SilentlyContinue | Unregister-ScheduledTask -Confirm:$false;
# Create new scheduled task
Register-ScheduledTask -Action $callPowerShell -Trigger $taskTrigger -TaskName $taskName -Description $description -User $username -Password $password -Settings $settingsCommand -RunLevel Highest;
}else{
write-host "$env:computername is too old. Just turn it off."
}
} -ArgumentList $scriptFile,$taskName,$description,$repeatIntervalMinutes,$username,$plainTextPassword,$uncServer
if($session){Remove-PSSession $session}
pause;
}
}else{
write-host "Please run this program with a valid Administrator account."
}
}
$adminCredential=obtainDomainAdminCredentials
setScheduledTasks -computernames $computers -scriptFile $scriptFile -description $description `
-repeatIntervalMinutes $repeatIntervalMinutes -taskName $taskName -credential $adminCredential
Categories: