I am taking over a process (originally created by a vendor) that creates and configures a VM. The process can either create a VM in hyperv on a physical host, or in vsphere. This is a powershell script called migrate-storeservers.ps1.
As part of the configuration, migrate-storeservers.ps1 calls a script locally on the VM that performs database validation (Validate-StoreServerDB.ps1). In the event that the VM is created in vsphere, we only want certain validation tasks to run.
I added a vsphere switch to the Migrate-StoreServers.ps1, and added logic to the function that calls the database validation script.
Vsphere switch at top of script:
[cmdletbinding()]
Param (
[Parameter(Mandatory=$true)]$Store,
[switch]$FinalizeVm,
[switch]$Vsphere,
[switch]$SqlSafe,
[switch]$FullAuto,
[switch]$TestRun,
$ErrorActionPreference = "Stop"
)
Function that calls database validation script, where $taskArgs are set based on Vsphere switch being used or not:
function validateDatabase($newServer, $newVmSession, $validateScript, $userScript, $moduleFile, $outLog){
$global:currentTask = "Database Validation"
Write-Debug "Current Task: $global:CurrentTask"
Write-Host "Starting database validation on server: $newServer"
$startTime = Get-Date
Write-Host "Start time: $startTime" -ForegroundColor Green
stageAaronsLiteModule -inModuleFile $moduleFile -inSession $newVmSession
$dateString = Get-Date -Format "yyyyMMddTHHmmss"
$logName = $("DBValidate_" + $newServer.ToUpper() + "_" + $dateString + ".log")
if($Vsphere){
$taskArg = "-NoProfile -ExecutionPolicy Unrestricted -File {0} -Store {1} -LogPath {2} -LogFile {3} -Vsphere {4}" `
-f $validateScript, $newServer, $outLog, $logName, $Vsphere
} else{
$taskArg = "-NoProfile -ExecutionPolicy Unrestricted -File {0} -Store {1} -LogPath {2} -LogFile {3}" `
-f $validateScript, $newServer, $outLog, $logName
}
executeTask -inTaskName "DatabaseValidation" -inPsSession $newVmSession -inUserScript $userScript -inTaskArg $taskArg `
-inMessage "Database Validation Script is starting. This takes about 2 minutes" -inCred $cred
Start-Sleep -Seconds 5
$logFound = copyLogToSource -inServer $newServer -inLog $($outLog + "\" + $logName) -outLogPath $LocalLogPath
if(-not($logFound)){
exitScript -inMessage "ERROR: Unable to find/copy log file" -failed $true
}
Start-Sleep 5
parseLogForErrors -inLog $($LocalLogPath + "\" + $logName) -errorString "TerminatingError"
$endTime = (Get-Date) - $startTime
Write-Host "Validate Database completed in: $endTime" -ForegroundColor Green
}
In the validation script being called by the function I added a vsphere switch and logic that sets the tasks list based on if the vsphere switch is used.
[cmdletbinding()]
Param (
[Parameter(Mandatory=$true)]$Store,
[Parameter(Mandatory=$true)]$LogPath,
[Parameter(Mandatory=$true)]$LogFile,
[switch]$Vsphere,
$ErrorActionPreference = "Stop"
)
########### Start Global Variable Section ###########
$sqlInstance = ($Store.ToUpper() + "\POS")
$sqlDatabase = "master"
$sqlConnectionTimeout = 60
$sqlQueryTimeout = 240
if($Vsphere){
$tasks = #(
"GoNoGo_Store2016_Upgrade_10_CheckServerName",
"GoNoGo_Store2016_Upgrade_20_CheckSQLVersion",
"GoNoGo_Store2016_Upgrade_30_CheckDBOnline",
"GoNoGo_Store2016_Upgrade_50_CheckDBExists"
)
} else{
$tasks = #(
"GoNoGo_Store2016_Upgrade_10_CheckServerName",
"GoNoGo_Store2016_Upgrade_20_CheckSQLVersion",
"GoNoGo_Store2016_Upgrade_30_CheckDBOnline",
"GoNoGo_Store2016_Upgrade_40_CheckSysMergeArcticles",
"GoNoGo_Store2016_Upgrade_50_CheckDBExists",
"GoNoGo_Store2016_Upgrade_60_CheckReplicationStatus"
)
}
My question is does the logic in the migrate-storeservers.ps1 look like it will pass the vsphere switch correctly to the validate-storeserverdb.ps1?
I am rather new to more advanced powershell like this and am concerned that the $taskArg for when the Vsphere switch is used are maybe not formatted correctly to pass the vsphere switch. The $taskArg for if the switch is not used was how the script was formatted before I added the switch logic.
Any input will be greatly appreciated.
Related
#===VARIABLES===##
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition # Gets the current directory
$TempPath=$env:temp
$Installer="$ScriptPath\Workstation.msi" # MSI name
# Use for new installs and upgrades. DO NOT CHANGE!!
$ArgumentList = " /i /quiet" # Standard arguments
##===LOGGING===##
Start-Transcript -Path "$($env:ProgramData)\WEIntune\AdminByRequest.log" -Append # Generate log
Write-Host "Starting script:" $(Get-Date -format 'MM/dd/yy HH:mm:ss')
##===INSTALL MSI===##
Write-Host "Installation Start Time:" $(Get-Date -format 'MM/dd/yy HH:mm')
Write-Host "Source Files:"$ScriptPath
$InstallTime = Measure-Command {start-process $Installer -wait -argumentlist $ArgumentList} # Call setup with argument & measure install time
$FinalInstallTime=[math]::Round($InstallTime.TotalMinutes,2) # Cleanup time format
EXIT 0 # Exit with Return Code 0
please help to run this scipt ,wanted to install MSI file
i have a script made in powershell and i am using nssm to create as a service to be executed every "x" time, however when starting the service it generates error and does not execute.
I have full administrator rights and I even tried to run PowerShell as an administrator without success.
If I run the script directly it works, however using nssm it is not working.
The error that happens is this:
Start-Service: Service 'nice (nice)' start failed.
At C: \ Program Files \ NICE Systems \ nssm.ps1: 10 char: 14
Start-Service <<<< $ serviceName
CategoryInfo: OpenError: (System.ServiceProcess.ServiceController: ServiceController) [Start-Service],
ServiceCommandException
FullyQualifiedErrorId: StartServiceFailed, Microsoft.PowerShell.Commands.StartServiceCommand
nssm.ps1
$nssm = (Get-Command nssm.exe).Definition
$serviceName = 'nice'
$powershell = (Get-Command powershell.exe).Definition
$scriptPath = 'C:\Program Files\NICE Systems\script_delecao.ps1'
$arguments = '-ExecutionPolicy Bypass -NoProfile -File "{0}"' -f $scriptPath
& $nssm install $serviceName $powershell $arguments
& $nssm status $serviceName
Start-Service $serviceName
Get-Service $serviceName
script_delecao.ps1
$logPath = "C:\Program Files\NICE Systems\Logs\*\Archive\*"
# -------------------------------------------------------------------------------------------
# SET $NDAYS WITH THE NUMBER OF DAYS TO KEEP IN LOG FOLDER.
$nDays = 180
# -------------------------------------------------------------------------------------------
# SET $EXTENSIONS WITH THE FILE EXTENSION TO DELETE.
# YOU CAN COMBINE MORE THAN ONE EXTENSION: "*.LOG, *.TXT,"
$Extensions = "*.log*"
# -------------------------------------------------------------------------------------------
# PAY ATTENTION! IF YOU COMBINE MORE THAN ONE LOG PATH AND EXTENSIONS,
# MAKE SURE THAT YOU ARE NOT REMOVING FILES THAT CANNOT BE DELETED
# -------------------------------------------------------------------------------------------
$PathDelete = "C:\Program Files\NICE Systems\Delecoes"
while ($true) {
If(!(test-path $PathDelete))
{
New-Item -ItemType Directory -Force -Path $PathDelete
}
$LogDate = (Get-Date).ToString("dd_MM_yyyy")
$DateTime = (Get-Date).ToString("yyy-MM-ddThh:mm:ss")
$Files = Get-Childitem $LogPath -Include $Extensions -Recurse | Where `
{$_.LastWriteTime -le (Get-Date).AddDays(-$nDays)}
foreach ($File in $Files)
{
if ($File -ne $NULL)
{
$Log = $DateTime + " - O arquivo " + $File + " foi deletado "
$Log | Out-File -Append $PathDelete\DeleteLogFile_$LogDate.log
Remove-Item $File.FullName| out-null
}
}
# Add a sleep at the end of the loop to prevent the script from eating
# too much CPU time
$Log = $DateTime + " FINAL DO ARQUIVO "
$Log | Out-File -Append $PathDelete\DeleteLogFile_$LogDate.log
Start-Sleep -Seconds 300
}
I believe I have a similar scenario where I cannot back-up Bamboo file system while it's running. My back-up executes from a rundeck server via Remote PowerShell, and even though the user has local admin rights it cannot stop and start services using NSSM. So I use this function to run the command elevated
ELEVAT "nssm stop bamboo"
tar --exclude=./logs --exclude=./temp --exclude=*.log --exclude=*.jar --verbose -czf E:\dropfolder\bamboo-home.tar.gz --directory=E:\bamboo-home .
ELEVAT "nssm start bamboo"
the function itself...
function ELEVAT ($command) {
$scriptBlock = [scriptblock]::Create($command)
configuration elevated {
Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
Set-StrictMode -Off
Node localhost {
Script execute {
SetScript = $scriptBlock
TestScript = {
if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Verbose "Verified Elevated Session"
return $false
} else {
Write-Verbose "Not an Elevated Session!"
exit 9996
}
}
GetScript = { return #{ 'Result' = 'RUN' } }
}
}
}
$mof = elevated
Start-DscConfiguration ./elevated -Wait -Verbose -Force
if ( $error ) { Write-Host "[ELEVAT][WARN] `$Error[] = $Error" ; $Error.clear()
}
}
I need some way to be able to run the below script as Administrator.
Script to get the Security Event log:
$DateAfter = (Get-Date).AddDays(-1)
$DateBefore = (Get-Date)
$EventLogTest = Get-EventLog -LogName Security -InstanceId 4625 -Before $DateBefore -After $DateAfter -Newest 5
$WinEventTest = Get-WinEvent -FilterHashtable #{ LogName = 'Security'; Id = 4625; StartTime = $DateAfter; EndTime = $DateBefore } -MaxEvents 5
Write-Host "$EventLogTest result is: "
$EventLogTest
Write-Host "$WinEventTest result is: "
$WinEventTest
I have compiled the below snippets, but somehow, the result is not displayed or nothing?
Combined Script:
$Role = "Domain Admins"
$CurrentLoginPrincipal = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent())
$IsDomainAdminGroupMember = $CurrentLoginPrincipal.IsInRole($Role)
$IsLocalComputerAdminMember = $CurrentLoginPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
If( -not ($IsDomainAdminGroupMember -and $IsLocalComputerAdminMember) ) {
Write-Warning "You are not running this as $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN). The script will be re-executed as Local Administrator"
Try {
Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"" -Verbose
}
Catch {
Write-Warning -Message "[PROCESS] Something wrong happened"
Write-Warning -Message $Error[0].Exception.Message
$out.Details = $_.Exception.Message
Write-Host " ERROR: $($out.Details)" -ForegroundColor Red
}
}
Else {
#a user running the script has the Domain Admins and Local PC Admin rights
Write-Host " $($CurrentLoginPrincipal.Identity.Name.ToString()) is currently member of $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN) " -ForegroundColor Green
}
$DateAfter = (Get-Date).AddDays(-1)
$DateBefore = (Get-Date)
$EventLogTest = Get-EventLog -LogName Security -InstanceId 4625 -Before $DateBefore -After $DateAfter -Newest 5
$WinEventTest = Get-WinEvent -FilterHashtable #{ LogName = 'Security'; Id = 4625; StartTime = $DateAfter; EndTime = $DateBefore } -MaxEvents 5
Write-Host "$EventLogTest result is: "
$EventLogTest
Write-Host "$WinEventTest result is: "
$WinEventTest
However, it is still not executing as Administrator to get the result displayed. How can I fix this?
First thing I noticed is that your if condition is wrong. It uses -and where that should be or (because either a Domain admin OR a local Administrator can run this)
Next, the arguments for Start-Process are incorrect. Personally, I like using the -ArgumentList as array.
Finally, in the catch block you use an undefined variable $out with an equally undefined property $out.Details. In the code below I have changed that to simply re-throw the exception.
Starting from where the if..else is:
if( -not ($IsDomainAdminGroupMember -or $IsLocalComputerAdminMember) ) {
Write-Warning "You are not running this as $($Role) or Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN). The script will be re-executed as Local Administrator"
# give the user some time to see this message
Start-Sleep 4
# Build base arguments for powershell.exe as string array
$argList = '-NoLogo', '-NoProfile', '-NoExit', '-ExecutionPolicy Bypass', '-File', ('"{0}"' -f $PSCommandPath)
# Add script arguments if any
$argList += $MyInvocation.BoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key)", "$($_.Value)"}
try {
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $pwd -ArgumentList $argList -Verbose -ErrorAction Stop
# exit the current script.
exit # Use return if you want to keep this instance open aswell
}
catch {
throw
}
}
else {
#a user running the script has the Domain Admins and Local PC Admin rights
Write-Host " $($CurrentLoginPrincipal.Identity.Name.ToString()) is currently member of $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN) " -ForegroundColor Green
}
I'm writing a PowerShell script that uses parameters / arguments and needs to be run as administrator but I'm trying to make it as user-friendly as possible so I'm trying to write it so that if it wasn't run as administrator then it can auto-elevate itself and preserve the original parameters (only switches and strings).
I was not able to find a solution online, hence this post.
I managed to accomplish this by "stringifying" $PsBoundParameters then using that with Start-Process PowerShell -Verb Runas -ArgumentList.
Note: $PsBoundParameters uses the parameters of the current scope ("root" vs inside a function, for example) so if you need to reference the command-line parameters (as I do) then you'll need to either use this variable outside of a function or first pass the variable to the function (as I've done here).
I've created a demonstration of this:
Param(
[switch]$ExampleSwitch,
[string]$ExampleString
)
Function Restart ($AllParameters, $Admin) {
$AllParameters_String = "";
ForEach ($Parameter in $AllParameters.GetEnumerator()){
$Parameter_Key = $Parameter.Key;
$Parameter_Value = $Parameter.Value;
$Parameter_Value_Type = $Parameter_Value.GetType().Name;
If ($Parameter_Value_Type -Eq "SwitchParameter"){
$AllParameters_String += " -$Parameter_Key";
} Else {
$AllParameters_String += " -$Parameter_Key $Parameter_Value";
}
}
$Arguments = "-File `"" + $PSCommandPath + "`" -NoExit" + $AllParameters_String;
If ($Admin -Eq $True){
Start-Process PowerShell -Verb Runas -ArgumentList $Arguments;
} Else {
Start-Process PowerShell -ArgumentList $Arguments;
}
}
$RanAsAdministrator = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator);
Write-Host "ExampleSwitch value:" $ExampleSwitch;
Write-Host "ExampleString value:" $ExampleString;
Write-Host "";
If ($RanAsAdministrator -Eq $True){
Write-Host "Running as administrator: Yes.";
} Else {
Write-Host "Running as administrator: No.";
}
$Elevate = Read-Host "Restart as current user or admin? (u/a)";
Write-Host "";
If ($Elevate -Like "u"){
Restart $PsBoundParameters;
} ElseIf ($Elevate -Like "a") {
Restart $PsBoundParameters -Admin $True;
}
Start-Sleep -Seconds 9999;
Param(
[Switch]$MySwitch,
[String]$MyString,
[Switch]$NoExit
)
If (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$PSHost = If ($PSVersionTable.PSVersion.Major -le 5) {'PowerShell'} Else {'PwSh'}
Start-Process -Verb RunAs $PSHost (#(' -NoExit')[!$NoExit] + " -File `"$PSCommandPath`" " + ($MyInvocation.Line -split '\.ps1[\s\''\"]\s*', 2)[-1])
Break
}
Write-Host "MySwitch:" $MySwitch;
Write-Host "MyString:" $MyString;
Explanation:
($MyInvocation.Line -split '\.ps1[\s\''\"]\s*', 2)[-1]) will resolve the current parameters
$MyHost determines the current PowerShell Host (PowerShell for Windows or PowerShell Core) and use the same host for the elevated window.
The -NoExit switch will prevent the elevated window to automatically close
Example:
.\RunAsAdministrator.ps1 -MyString "Test 123" -MySwitch -NoExit
I put together a powershell function that schedules server reboots.
I'm stuck on adding a for loop to run the function for multiple servers using ParameterSetName.
Before adding the ParamSets, I just setup a function and called the function in the same script within a for loop which worked great.
Function ScheduleReboot{
[CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName="ViewOnly")]
Param(
[Parameter(Mandatory=$false,ParameterSetName="KillSwitch")]
[Switch]$NukeJobs,
[Parameter(Mandatory=$true,
HelpMessage="ServerName goes here",ParameterSetName="FullRun")]
[string]$server,
[Parameter(Mandatory=$true,
HelpMessage="Enter a Date/Time 07-28-15 16:00 For July 28th, 2015 at 4:00 PM",
ParameterSetName="FullRun"
)]
[ValidatePattern('(?#Enter MM-dd-YY HH:MM 24hr clock)\d{2}-\d{2}-\d{2}\s\d{2}[:]\d{2}')]
$date,
[Parameter(Mandatory=$false,ParameterSetName="ViewOnly")]
[switch]$ViewOnly
) #"$server will reboot on $date"
Switch ($PSCmdlet.ParameterSetName){
"KillSwitch"{
if($env:PROCESSOR_ARCHITECTURE -eq "x86")
{
set-alias ps64 "$env:windir\sysnative\WindowsPowerShell\v1.0\powershell.exe"
$script2 = [ScriptBlock]::Create("IF(Test-Path -Path C:\Windows\System32\Tasks\Microsoft\Windows\PowerShell\ScheduledJobs\$server)
{Remove-Item -Force -Path C:\Windows\System32\Tasks\Microsoft\Windows\PowerShell\ScheduledJobs\$server}")
"Removing previous scheduled server reboot data"
ps64 -command $script2
} else {
Get-ScheduledJob | Unregister-ScheduledJob
}
}#End killswitch block.
"FullRun" {
[array]$servers = gc D:\Scripts\servers.txt
[array]$dates = gc D:\Scripts\dates.txt
if($dates.Length -eq $servers.Length)
{
"Input count matches"
for($i=0;$i -lt $servers.Length;$i++)
{
$day = $dates[$i]
$comp = $servers[$i]
$user = Get-Credential -UserName $env:USERNAME -Message "UserName/password for scheduled Reboot"
$trigger = New-JobTrigger -once -at $day
$script = [ScriptBlock]::Create("D:\Scripts\Scheduled-Reboot-Single.ps1 -server $server | Out-File -Force \\SysLogSvr\d$\scripts\$server-Reboot.log")
Register-ScheduledJob -Name $comp -Credential $user -Trigger $trigger -ScriptBlock $script
}#end for loop
} else {
$warn = "Server list contains " + $servers.Count + " items and Date list contains " + $dates.Count + " items, Please re-check!!!"
Write-Warning $warn
}
}#end fullrun block.
"ViewOnly" {
Get-ScheduledJob | Get-JobTrigger
Get-ScheduledJob | Select Name,Command | FT -AutoSize
}#end viewonly block.
}#End param switch
}#end function.
Make that 3 (or more) different functions. The parameter sets the way you are using it is over-complicating it and making it hard for you to debug it.
I would have made a Get-ScheduledReboot, Clear-ScheduledReboot, Set-ScheduledReboot collection of functions.
There are a few bugs in this script as posted that are causing you issues. The first is the nuke parameter set does not contain the server parameter. The other bug is the one that is throwing you off. You define a $server parameter above but in the function, you change gears and load from a text file. You are mixing $server, $servers and $comp variables around causing it to fail.
You should change your [string]$server to [string[]]$server or better yet, [string[]$ComputerName and foreach($node in $ComputerName){...} on the collection. Keep the text file loading outside that function.