Azure Custom Script Extension continually restarting - windows

Having a bit of trouble with the Azure Custom script extension on a windows VM. I have created a script that installs a particular business application. In order to accomplish this, the script calls several other scripts and a reboot is required in between two scripts.
The script is designed such that it can "pick up where it left off" by checking whether certain files exist or if registry keys exist. The reboot portion of this goes fine, however, I'm noticing that the script handler sets several registry keys to keep track of its state in
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Azure\ScriptHandler
I see a registry value called "MreSeqNumStarted", which I presume is how the script handler keeps track of what script it's executing. There is only one script fired by the extension currently, and this value is set to 0.
What I'm seeing, is that, when the machine is rebooted after the script has completed, the script fires again. This occurs with every reboot of the virtual machine. I believe the script extension is not marking the script as "completed", and I'm somewhat at a loss as to why. The basic (sanitized) flow of the script is below:
trap {$_.Exception.Message; Get-PSCallstack;continue}
if(Test-Path C:\ScriptLog.txt)
{
Add-Content "C:\ScriptLog.txt" "`nScript Previously Run, Prior log exists."
} else {
Write-Host "Begin Script" | Out-File "C:\ScriptLog.txt"
}
#$storagePath = [uri]::EscapeDataString($storagePath)
#Create Directory Structure and download scripts/files
if(Test-Path C:\PreparePlatform\) {
Add-Content "C:\ScriptLog.txt" "`nFiles Already Downloaded... Continuing"
} else {
New-Item -ItemType Directory -Force -Path C:\SoftwareName\PreparePlatform\
(New-Object Net.WebClient).DownloadFile('SAS-URL-GOES-HERE','C:\SoftwareName\PreparePlatform\PreparePlatform.zip');(new-object -com shell.application).namespace('C:\SoftwareName\PreparePlatform').CopyHere((new-object -com shell.application).namespace('C:\SoftwareName\PreparePlatform\PreparePlatform.zip').Items(),16)
(New-Object Net.WebClient).DownloadFile('SAS-URL-GOES-HERE','C:\SoftwareName\SOMEZIP.zip');(new-object -com shell.application).namespace('C:\SoftwareName').CopyHere((new-object -com shell.application).namespace('C:\SoftwareName\SOMEZIP.zip').Items(),16)
Add-Content "C:\ScriptLog.txt" "`nExtracted Install Media"
}
if(Test-Path C:\SoftwareName\preparedone.txt) {
Add-Content "C:\ScriptLog.txt" "`nPreparePlatform already completed... Continuing"
} else {
Invoke-Expression "Set-Location c:\SoftwareName\PreparePlatform\; C:\SoftwareName\PreparePlatform\Prepare-WindowsPlatform.ps1"
CreateSQLUser -identifierValue $value -saPassword $sqlSaPassword -tenantPass $TenantSqlPassword
CreateSQLLogin -identifierValue $value -saPassword $SqlSaPassword -tenantPass $TenantSqlPassword
Write-Host "PreparePlatform Complete" | Out-File "C:\SoftwareName\preparedone.txt"
}
if(Test-Path C:\SoftwareName\UpdatePlatform.txt) {
Add-Content "C:\ScriptLog.txt" "`nUpdatePlatform already completed... Continuing"
} else {
Add-Content "C:\ScriptLog.txt" "`nBegin Update-Platform"
Invoke-Expression "Set-Location C:\SoftwareName\PreparePlatform\; C:\SoftwareName\PreparePlatform\Update-WindowsPlatform.ps1"
Write-Host "UpdatePlatform Complete" | Out-File "C:\SoftwareName\UpdatePlatform.txt"
}
if(Test-Path C:\SoftwareName\Rebooted.txt) {
Add-Content "C:\ScriptLog.txt" "`nReboot already completed... Continuing"
} else {
Write-Host "Rebooting" | Out-File "C:\SoftwareName\Rebooted.txt"
Restart-Computer -Force
Restart-Computer -Force
Start-Sleep 3600 #Make sure nothing else executes
}
# TODO: Make more robust for potentially more nodes... need to delay 1 hour for each node
If($env:computername -eq 'pvm-SoftwareName-1') { Write-Output "Second Node, waiting 1 hour"; Add-Content "C:\ScriptLog.txt" "`nNot First Node, waiting 1 hour" ;Start-Sleep -Seconds 3600 }
Add-Content "C:\ScriptLog.txt" "`nPrepare SoftwareName Installers"
Set-Location C:\SoftwareName\Script\; C:\SoftwareName\Script\Prepare-SoftwareNameInstallers.ps1 -neededParameters $values
Add-Content "C:\ScriptLog.txt" "`nPrepare SoftwareName Installers complete"
try
{
if(-not (Get-Service -Name 'SoftwareName server')) {
C:\SoftwareName\Script\Install-SoftwareNameBP.ps1 -neededParameters $values
} else {
Add-Content "C:\ScriptLog.txt" "`n ALREADY INSTALLED!"
}
}
catch
{
$_.Exception.Message;Get-PSCallStack
if (-not (Get-Service -Name 'SoftwareName Server').Status -eq 'Running')
{throw "Service unavailable"}
}
Write-Output "Install Finished."
try
{
if(-not ((Invoke-WebRequest -Uri https://IISSiteURL/).StatusCode -eq '200'))
C:\SoftwareName\Script\Install-SoftwareNameSF.ps1 -neededParameters $values
}
catch
{
$_.Exception.Message;Get-PSCallStack
if(-not (Test-Path -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{AGUID}") )
{throw "Smartforms Install failed"}
}
try
{
#Begin
C:\SoftwareName\Script\Install-SoftwareNameSP.ps1 -neededParameters $values
}
catch
{
$_.Exception.Message;Get-PSCallStack
if(-not (Test-Path -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{AGUID}") )
{throw "Sharepoint Install failed"}
}
try
{
#[bool]$skipDeploy
#if($env:computername -eq 'pvm-SoftwareName-0') { $skipDeploy = $false } else { $skipDeploy = $true }
#Begin
C:\SoftwareName\Script\Install-SoftwareNameMG.ps1 -neededParameters $values
catch
{
$_.Exception.Message;Get-PSCallStack
if(-not (Test-Path -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{AGUID}") )
{throw "Management Install failed"}
}
try
{
C:\SoftwareName\Script\Install-Finalize.ps1 -neededParameters $values
}
catch
{
$_.Exception.Message;Get-PSCallStack
if(-not (Test-Path -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{AGUID}") )
{throw "Install Finalize Failed"}
}
#Kill the script
Add-Content "C:\ScriptLog.txt" "`nScript Completed"
Exit
Below is the expected output of C:\ScriptLog.txt
Begin Scheduled Job Regstration
Extracted Install Media
Begin PreparePlatform
Completed PreparePlatform
Begin Update-Platform
Script Previously Run, Prior log exists.
Files Already Downloaded... Continuing
PreparePlatform already completed... Continuing
UpdatePlatform already completed... Continuing
Reboot already completed... Continuing
Prepare SOFTWARENAME Installers
Prepare SOFTWARENAME Installers complete
C:\SOFTWARENAME\Script\Install-K2BP.ps1 -parameterValues $values
C:\SOFTWARENAME\Script\Install-K2SF.ps1 -parameterValues $values
C:\SOFTWARENAME\Script\Install-K2SP.ps1 -parameterValues $values
C:\SOFTWARENAME\Script\Install-K2MG.ps1 -parameterValues $values
C:\SOFTWARENAME\Script\Install-Finalize.ps1 -parameterValues $values
Script Completed
After a reboot, the same log file:
Begin Scheduled Job Regstration
Extracted Install Media
Begin PreparePlatform
Completed PreparePlatform
Begin Update-Platform
Script Previously Run, Prior log exists.
Files Already Downloaded... Continuing
PreparePlatform already completed... Continuing
UpdatePlatform already completed... Continuing
Reboot already completed... Continuing
Prepare SOFTWARENAME Installers
Prepare SOFTWARENAME Installers complete
C:\SOFTWARENAME\Script\Install-SOFTWARENAMEBP.ps1 -neededParameters $values
C:\SOFTWARENAME\Script\Install-SOFTWARENAMESF.ps1 -neededParameters $values
C:\SOFTWARENAME\Script\Install-SOFTWARENAMESP.ps1 -neededParameters $values
C:\SOFTWARENAME\Script\Install-SOFTWARENAMEMG.ps1 -neededParameters $values
C:\SOFTWARENAME\Script\Install-Finalize.ps1 -neededParameters $values
Script Completed
Script Previously Run, Prior log exists.
Files Already Downloaded... Continuing
PreparePlatform already completed... Continuing
UpdatePlatform already completed... Continuing
Reboot already completed... Continuing
Prepare SOFTWARENAME Installers
Prepare SOFTWARENAME Installers complete
C:\SOFTWARENAME\Script\Install-SOFTWARENAMEBP.ps1 -neededParameters $values...
Edit1: I've commented out all calls to external scripts. The script at this point only writes output to a file effectivley and traps any exceptions in doing so. The script continues to re-fire with every VM reboot.

Related

Is there a script I can use to upgrade Windows 10 to the latest version silently?

I have been trying to get a script working that will upgrade Windows 10 to its latest version (22H2) silently, and reboot once it has finished downloading so it can install. I tried the script I included in this post, but the computer did not reboot on its own. When I checked the log file, "Will try to download Windows Update Assistant..", was the last entry that the task logged. The weird thing is, when I reboot the computer, the upgrade starts. I'm new to this and haven't been able to figure out why the computer will not finish the download and then reboot to install. Can anyone help me get this script to work?
#set computer to not go to sleep
powercfg.exe -x -monitor-timeout-ac 0
powercfg.exe -x -monitor-timeout-dc 0
powercfg.exe -x -disk-timeout-ac 0
powercfg.exe -x -disk-timeout-dc 0
powercfg.exe -x -standby-timeout-ac 0
powercfg.exe -x -standby-timeout-dc 0
powercfg.exe -x -hibernate-timeout-ac 0
powercfg.exe -x -hibernate-timeout-dc 0
function Write-Log {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$Message
)
try {
if (!(Test-Path -path ([System.IO.Path]::GetDirectoryName($LogFilePath)))) {
New-Item -ItemType Directory -Path ([System.IO.Path]::GetDirectoryName($LogFilePath))
}
$DateTime = Get-Date -Format ‘yyyy-MM-dd HH:mm:ss’
Add-Content -Value "$DateTime - $Message" -Path $LogFilePath
}
catch {
Write-Error $_.Exception.Message
}
}
Function CheckIfElevated() {
Write-Log "Info: Checking for elevated permissions..."
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
[Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Log "ERROR: Insufficient permissions to run this script. Open the PowerShell console as an administrator and run this script again."
return $false
}
else {
Write-Log "Info: Code is running as administrator — go on executing the script..."
return $true
}
}
# Main
try {
# Declarations
[string]$DownloadDir = 'C:\Temp\Windows_FU\packages'
[string]$LogDir = 'C:\Temp\Windows_FU\Logs'
[string]$LogFilePath = [string]::Format("{0}\{1}_{2}.log", $LogDir, "$(get-date -format `"yyyyMMdd_hhmmsstt`")", $MyInvocation.MyCommand.Name.Replace(".ps1", ""))
[string]$Url = 'https://go.microsoft.com/fwlink/?LinkID=799445'
[string]$UpdaterBinary = "$($DownloadDir)\Win10Upgrade.exe"
[string]$UpdaterArguments = '/quietinstall /skipeula /auto upgrade /copylogs $LogDir'
[System.Net.WebClient]$webClient = New-Object System.Net.WebClient
# Here the music starts playing ..
Write-Log -Message ([string]::Format("Info: Script init - User: {0} Machine {1}", $env:USERNAME, $env:COMPUTERNAME))
Write-Log -Message ([string]::Format("Current Windows Version: {0}", [System.Environment]::OSVersion.ToString()))
# Check if script is running as admin and elevated
if (!(CheckIfElevated)) {
Write-Log -Message "ERROR: Will terminate!"
break
}
# Check if folders exis
if (!(Test-Path $DownloadDir)) {
New-Item -ItemType Directory -Path $DownloadDir
}
if (!(Test-Path $LogDir)) {
New-Item -ItemType Directory -Path $LogDir
}
if (Test-Path $UpdaterBinary) {
Remove-Item -Path $UpdaterBinary -Force
}
# Download the Windows Update Assistant
Write-Log -Message "Will try to download Windows Update Assistant.."
$webClient.DownloadFile($Url, $UpdaterBinary)
# If the Update Assistant exists -> create a process with argument to initialize the update process
if (Test-Path $UpdaterBinary) {
Start-Process -FilePath $UpdaterBinary -ArgumentList $UpdaterArguments -Wait
Write-Log "Fired and forgotten?"
}
else {
Write-Log -Message ([string]::Format("ERROR: File {0} does not exist!", $UpdaterBinary))
}
}
catch {
Write-Log -Message $_.Exception.Message
Write-Error $_.Exception.Message
}

How to compare two Foreach loops

My current script checks if a specific folder on some clients exists.
I'd like to check if the client is online or offline before checking if the folder exists.
My current script looks like this:
$CDS = Get-content C:\Users\XY\Desktop\Clientliste.txt
Foreach($c in $CDS) {
IF (Test-Connection -BufferSize 32 -Count 1 -ComputerName $c -Quiet) {
Foreach ($c in $CDS) {
$Test = Test-Path -path "\\$c\c$\apps\perl"
Start-Sleep -s 0.25
If ($Test -eq $True) {
Write-Host "Path exists on $c."
}
Else {
Write-Host "Path NOT exist on $c."
}
}
}
Else {
Write-Host "The remote computer " $c " is Offline"
}
}
I don't know how to link the foreach loops so that they work together.
Because when I run my script now, it goes after the first if request in the 2nd foreach loop and it does leave it first, when it finishes the 2nd foreach loop.
I don't want that. I want that if the client is online, it checks if the paths exists and then goes to the next client and checks again if it is online and then...
Maybe you can help me :)
Why do another identical loop over the items from the text file is you have already tested the machine is reachable?
Just remove that second loop and do:
$CDS = Get-Content -Path 'C:\Users\XY\Desktop\Clientliste.txt'
foreach($computer in $CDS) {
if (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet) {
if (Test-Path -Path "\\$computer\C$\apps\perl" -PathType Container) {
Write-Host "Path exists on computer '$computer'."
}
else {
Write-Host "Path NOT exist on computer '$computer'."
}
}
else {
Write-Host "The remote computer '$computer' is Offline"
}
}

File Lock Mechanism using PowerShell

I have two processes that reads from the same file. I want to make sure that those two processes doesn't run at the same. My approach is that , when a process starts, it should look for a file(process.txt). If the file doesn't exist, it creates the file and continue execution, reading from the shared file(sharedfile.txt) between the two processes. It then deletes the process.txt file after executing.
Since both process deletes process.txt file after execution, if the file exists,it should go into a start sleep until the other process finishes and deletes the file.
The problem here is when one process finishes and deletes the file(process.txt), the other still stay in the loop without executing even if no file exists. I am not sure of the right loop to use. I tried a couple of them and couldn't achieve my goal.
Clear Host
$sleeptime = 15
$lockfile = "C:\Users\processid.txt"
$file = test-path -Path $lockfile
Try
{
if($file -eq 'True')
{
echo “Lock file found!”
echo "This means file is being used by another process"
echo "Wait for file to be deleted/released"
echo “Sleeping for $sleeptime seconds”
Start-Sleep $sleeptime -Verbose
}
else
{
New-item -Path $lockfile
#Executing a code here
}
Remove-Item $lockfile –Force
}
}
Catch
{
Write-Host $_.Exception.Message`n
}
Consider this one, it creates a lock file and opens it for exclusive access. Let's you run your script, then cleans up afterwards. If access cannot be gained it waits 5 seconds before trying again.
Using this for both scripts should make them play nice and wait for their turn. If for some reason a lock file gets left behind (unplanned reboot that aborts script or similar) this one will still work fine as well.
$LockFile = 'C:\temp\test.lock'
# Loop that runs until we have exclusive write access to $LockFile
while ($FileStream.CanWrite -eq $false) {
if (-not (Test-Path -Path $LockFile)) {
Set-Content -Path $LockFile -Value 'Lockfile'
}
try {
$FileStream = [System.IO.File]::Open("C:\temp\test.lock",'Open','Write')
}
catch {
'Waiting 5 seconds'
Start-Sleep -Seconds 5
}
}
# ---
# Your actual script here
# ---
# Cleanup
$FileStream.Close()
$FileStream.Dispose()
Remove-Item -Path $LockFile -Force
You could also go one step further and instead lock the file you're actually using, but then you'll need to read it from the file stream since it will be locked for any cmdlets.
You could use a while loop like this
Clear Host
$sleeptime = 15
$lockfile = "C:\Users\processid.txt"
While(Test-Path -Path $lockfile){
Write-Host "Lock file found!"
Write-Host "This means file is being used by another process"
Write-Host "Wait for file to be deleted/released"
Write-Host "Sleeping for $sleeptime seconds"
Start-Sleep $sleeptime -Verbose
}
try{
New-item -Path $lockfile
#Executing a code here
}
catch{
Write-Host $_.Exception.Message`n
}
finally{
Remove-Item $lockfile –Force -ErrorAction SilentlyContinue
}

Powershell Script to deploy multiple software using variables

I am building a deployment script to install software on a new device using a ppkg file.
The script looks at which drive is the USB drive and copies the software over to the local temp folder and runs them according to a set of variables as shown below.
What I am struggling to do is simplify the script so I am not repeating code 7 times down the page, I want to just run a loop 7 times to pull in the needed software. I tried an array but I think I am not quite understanding it completely.
This is my script so far with the repeating code:
#SOE application Variables
#applcation1 CM_client
$app1name = "Config Manager Client 1706"
$app1skip = "no"
$app1path = "$env:SystemDrive\temp\soe\application_installs\app1\CM_client_inst_1706\"
$app1runcommand = "clientx64.bat"
$app1arguments = ""
#applcation2
$app2name = "Office 2016 Pro Plus"
$app2skip = "no"
$app2path = "$env:SystemDrive\temp\soe\application_installs\app2\O2016\"
$app2runcommand = "setup.exe"
$app2arguments = "/configure configuration.xml"
#log Folder
$datetime = Get-Date -format "yyyy.MM.dd-HH.mm.ss"
$logpath = "$env:ALLUSERSPROFILE\SOEInst_ppkg\$datetime"
New-Item -Path $logpath -ItemType Directory -ErrorAction SilentlyContinue
#Transcript Start
Start-Transcript -Path $logpath\SOE-app-installer-ppkg-$datetime.log
#Timer Function
$pkgremovetime = Get-Date -format "HH:mm:ss"
write-host "Script Start Time - $pkgremovetime"
#Find USB Drive
Write-host Discovering USB Drive
$drives = (GET-WMIOBJECT –query “SELECT * from win32_logicaldisk").DeviceID
foreach ($drive in $drives) {
$usbdrive = (dir $drive USBIMG.FILE | Select-Object -Unique "USBIMG.FILE")
if ($usbdrive -match "USBIMG.FILE*") {
$datadrive = $drive
}
}
Write-host Found $datadrive is the USB drive
#Copy Applications to Local Drive
Write-Host Creating Installer Folder
New-Item -Path $env:SystemDrive\temp\SOE -ItemType Directory
Copy-Item $datadrive\application_installs $env:SystemDrive\temp\soe -Recurse -Verbose
#Install Applications
#Application 1
if ($app1skip -eq "no") {
if ($app1arguments) { #Arguments Variable Populated
Write-Host Installing Applcation 1 `($app1name`)
$app1 = Start-Process -Wait -FilePath $app1path$app1runcommand -ErrorAction Continue -ArgumentList $app1arguments -WindowStyle Normal
if ($app1.ExitCode -eq "0") {
Write-Host $app1name Installed ok
} Else {
Write-host $app1name install exited with code $app1.ExitCode
}
}
}Else { #Argurments Variable Empty
Write-Host Installing Applcation 1 `($app1name`)
$app1 = Start-Process -Wait -FilePath $app1path$app1runcommand -ErrorAction Continue -WindowStyle Normal
if ($app1.ExitCode -eq "0") {
Write-Host $app1name Installed ok
} Else {
Write-host $app1name install exited with code $app1.ExitCode
}
}
#Application 2
if ($app2skip -eq "no") {
if ($app2arguments) { #Arguments Variable Populated
Write-Host Installing Applcation 2 `($app2name`)
$app2 = Start-Process -Wait -FilePath $app2path$app2runcommand -ErrorAction Continue -ArgumentList $app2arguments -WindowStyle Normal
if ($app2.ExitCode -eq "0") {
Write-Host $app2name Installed ok
} Else {
Write-host $app2name install exited with code $app2.ExitCode
}
}
}Else { #Argurments Variable Empty
Write-Host Installing Applcation 2 `($app2name`)
$app2 = Start-Process -Wait -FilePath $app2path$app2runcommand -ErrorAction Continue -WindowStyle Normal
if ($app2.ExitCode -eq "0") {
Write-Host $app2name Installed ok
} Else {
Write-host $app2name install exited with code $app2.ExitCode
}
}
#cleanup
Remove-Item $env:SystemDrive\temp\soe -Recurse -Force -Verbose
#get end time
$pkgremovetime_end = Get-Date -format "HH:mm:ss"
#calculate time difference
$timetaken = New-TimeSpan $pkgremovetime $pkgremovetime_end
if ($timetaken.Seconds -lt 0) {
$Hrs = ($timetaken.Hours) + 23
$Mins = ($timetaken.Minutes) + 59
$Secs = ($timetaken.Seconds) + 59 }
else {
$Hrs = $timetaken.Hours
$Mins = $timetaken.Minutes
$Secs = $timetaken.Seconds }
$Difference = '{0:00}:{1:00}:{2:00}' -f $Hrs,$Mins,$Secs
#log time difference
write-host "Script End Time - $pkgremovetime_end"
Write-Host "Total time taken $difference"
#Transcript End
Stop-Transcript
I suggest you make a function which takes in the variables. I did a quick comparison of your installation codes and something like this should work
function installApplication{
Param($skip, $arguments, $name, $path, $runcommand)
if ($skip -eq "no"){
if ($arguments){
write-host "Installing Application $appname"
$app = Start-Process -Wait -FilePath $path$runcommand -ErrorAction....
if($app.ExitCode -eq "0"){
....
....
}
and so on, You can then call the function using
installApplication $app1skip $app1arguments $app1name $app1path $app1runcommand
installApplication $app2skip $app2arguments $app2name $app2path $app1runcommand
Your input arguments will replace the function parameters in the order you pass them in, or you can use -skip $app1skip to assign the parameters.
If your repeating the same code too many times, I suggest throwing it into something like diffchecker, put the code into a function and replace all the differences with variables.
You can see your code here https://www.diffchecker.com/FxAIdD6g (1 Day only)

Run powershell script with elevated command

I have an autologon Powershell script that I'd like to run as admin when I double click on it. I tried to use different scripts but I'm out of luck.
For example:
Start-Process PowerShell –Verb RunAs
Would open another Powershell screen as administrator but without the original script that I wanna run which is:
net accounts /minpwlen:0
net user TPUser /add
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name AutoAdminLogon -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name DefaultUserName -Value "TPUser"
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name DefaultPassword -Value ""
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name DefautDomainName -Value ""
copy c:\temp\OP.rdp c:\Users\Public\Desktop
pause
Any idea how can I get this to work ?
You are in luck because I was fighting with this issue for some time, what you need to do is make it take note of where it is at and when it starts back up the shell as an admin it needs to execute the script.
Function Test-IsAdmin {
[cmdletbinding()]
Param()
Write-Verbose "Checking to see if current user context is Administrator"
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.NTAccount] "[WriteGroupHere]"))
{
Write-Warning "You are not currently running this under an Administrator account! `nThere is potential that this command could fail if not running under an Administrator account."
Write-Verbose "Presenting option for user to pick whether to continue as current user or use alternate credentials"
#Determine Values for Choice
$choice = [System.Management.Automation.Host.ChoiceDescription[]] #("Use &Alternate Credentials","&Continue with current Credentials")
#Determine Default Selection
[int]$default = 0
#Present choice option to user
$userchoice = $host.ui.PromptforChoice("Warning","Please select to use Alternate Credentials or current credentials to run command",$choice,$default)
#$workingDir = $PSCommandPath
#$PSCommandPath
Write-Debug "Selection: $userchoice"
#Determine action to take
Switch ($Userchoice)
{
0
{
#Prompt for alternate credentials
Write-Verbose "Prompting for Alternate Credentials"
$Credential = Get-Credential
#Write-Output $Credential
#We are not running "as Administrator" - so relaunch as administrator
Start-Process powershell.exe -ArgumentList "$PSCommandPath" -Credential $Credential
#-WorkingDirectory $workingDir
exit
}
1
{
#Continue using current credentials
Write-Verbose "Using current credentials"
Write-Output "CurrentUser"
}
}
}
Else
{
Write-Verbose "Passed Administrator check"
#$Host.UI.RawUI.WindowTitle = "Custom Powershell Environment" +
#$Host.UI.RawUI.BackgroundColor = "DarkBlue"
}
}
with this just put it in the top of your script and call the function, and you will need to change the group that it checks to know if you are an admin or not, I was using an AD group to check since it was a more functional way for me.
I have used the following before to re-launch as script as admin but there is not stopping the UAC prompt:
function IsAdministrator
{
$Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity)
$Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}
function IsUacEnabled
{
(Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System).EnableLua -ne 0
}
#
# Main script
#
if (!(IsAdministrator))
{
if (IsUacEnabled)
{
[string[]]$argList = #('-NoProfile', '-NoExit', '-File', $MyInvocation.MyCommand.Path)
$argList += $MyInvocation.BoundParameters.GetEnumerator() | Foreach {"-$($_.Key)", "$($_.Value)"}
$argList += $MyInvocation.UnboundArguments
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $pwd -ArgumentList $argList
return
}
else
{
throw "You must be administrator to run this script"
}
}
I actually used this script on top of mine and it worked perfectly.
# ##########################################
# Determine if we have Administrator rights
Write-Host 'Checking user permissions... '
$windowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$windowsSecurityPrincipal = New-Object System.Security.Principal.WindowsPrincipal($windowsID)
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
If (!($windowsSecurityPrincipal.IsInRole($adminRole))) {
Write-Warning 'Current user does not have Administrator rights'
Write-Host 'Attempting to copy files to temporary location and restarting script'
# Get random file name
Do {
$temp = [System.IO.Path]::GetTempPath() + [System.IO.Path]::GetRandomFileName()
} Until (!(Test-Path -LiteralPath "$temp"))
# Create directory
Write-Host 'Creating temp directory... ' -NoNewLine
New-Item -Path "$temp" -ItemType 'Directory' | Out-Null
Write-Host 'done.'
# Copy script to directory
Write-Host 'Copying script to temp directory... ' -NoNewLine
Copy-Item -LiteralPath "$($myInvocation.MyCommand.Path)" "$temp" | Out-Null
Write-Host 'done.'
$newScript = "$($temp)\$($myInvocation.MyCommand.Name)"
# Start new script elevated
Write-Host 'Starting script as administrator... ' -NoNewLine
$adminProcess = New-Object System.Diagnostics.ProcessStartInfo
$adminProcess.Filename = ([System.Diagnostics.Process]::GetCurrentProcess()).Path
$adminProcess.Arguments = " -File `"$newScript`""
$adminProcess.Verb = 'runas'
Try {
[System.Diagnostics.Process]::Start($adminProcess) | Out-Null
}
Catch {
Write-Error 'Could not start process'
Exit 1
}
Write-Host 'done.'
Exit 0
}

Resources