I am writing this powershell script for automating TFS deployment. Here is the section of the script that is throwing the error Missing statement block in switch statement clause. The syntax looks correct to me but I can't seem to resolve the error. Any ideas?
function core ([string]$EnVar)
{
# Set the build parameters
$params="environment="+$EnVar+";SQLServer="+$SQLServer+";IISServer="+$IISServer+";DBName="+$DBName
write-output $params >> $OutputFile
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe $DeploymentScript /p:$params >> $OutputFile 2>&1
write-output $params >> $OutputFile
write-output $LastExitCode >> $OutputFile
if ($LastExitCode -ne 0)
{
write-output "An error has occured." >> $OutputFile
$outline = "Updating build quality to Failed " + $Environment + " Deployment."
write-output $outline >> $OutputFile
switch ($EnVar)
{
"Test" {$build.Quality = "Failed Test Deployment"}
"Stage" {$build.Quality = "Failed Stage Deployment"}
"Prod" {$build.Quality = "Failed Production Deployment"}
default {$build.Quality = "Rejected"}
}
$build.Save()
$cmdLine = "/ID 2 /L ""Operation Logs"" /T ERROR /SO $tfsProject.DeployTo$EnVar /D ""($buildNumber) deployed by ($requestedby) FAILED! Deployment log file: $OutputFile """
invoke-expression "$tool $cmdline"
write-output "Sending failure email." >> $OutputFile
$to = $emailRequestedBy
$body = "<html>Deployment log file: """+ $OutputFile + """</html>"
$subject = $tfsProject + " " + $EnVar + " Deployment failed"
send-SMTPmail -to $to -from "tfsservice#vistex.com" -subject $subject -html -body $body
exit(1)
}
else
{
$outline = "Successfully deployed to " + $EnVar + "."
write-output $outline >> $OutputFile
$outline = "Updating build quality to Deployed to " + $EnVar + "."
write-output $outline >> $OutputFile
switch ($EnVar)
{
"Test" ($build.Quality = "Deployed to Test"}
"Stage" {$build.Quality = "Deployed to Stage"}
"Prod" {$build.Quality = "Deployed to Production"}
default {$build.Quality = "Rejected"}
}
$build.Save()
$cmdLine = "/ID 1 /L ""Operation Logs"" /T SUCCESS /SO $tfsProject.DeployTo$EnVar /D ""($buildNumber) deployed by $requestedby successfully finish. Deployment log file: $OutputFile """
invoke-expression "$tool $cmdline"
write-output "Sending success email." >> $OutputFile
$to = $emailRequestedBy
$body = "<html>Deployment log file: """+ $OutputFile + """</html>"
$subject = $tfsProject + " " + $EnVar + " Deployment successfully completed"
send-SMTPmail -to $to -from "tfsservice#vistex.com" -subject $subject -html -body $body
}
}
Found the issue. It's a typo... You have a ( instead of a { in the "Test" case:
else
{
$outline = "Successfully deployed to " + $EnVar + "."
write-output $outline >> $OutputFile
$outline = "Updating build quality to Deployed to " + $EnVar + "."
write-output $outline >> $OutputFile
switch ($EnVar)
{
"Test" ($build.Quality = "Deployed to Test"}
"Stage" {$build.Quality = "Deployed to Stage"}
"Prod" {$build.Quality = "Deployed to Production"}
default {$build.Quality = "Rejected"}
}
Update the line to be:
"Test" {$build.Quality = "Deployed to Test"}
Related
I'm brand new to PowerShell and am working on modifying a script to combine 4 functions into one. I am having a little trouble understanding how all the pieces of the individual functions fit together. For example, it has a $msg variable that doesn't seem to be declared anywhere else in the script. So essentially i'm looking for any advice on how to make these fit.
##LogSuccess function
##Display provided message as a SUCCESS in green, with SUCCESS: prefix
##If logging mode is set to true, also write SUCCESS message to $logfileSS
Function global:LogSuccess($msg){
Write-Host "SUCCESS: " $msg -ForegroundColor "Green"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output "SUCCESS: " $msg | Out-File -filepath $global:logfile -Append
}
}
##LogError function
##Display provided message as an error in red, with ERROR: prefix
##If logging mode is set to true, also write ERROR message to $logfile
Function global:LogError($msg){
Write-Host "ERROR: " $msg -ForegroundColor "Red"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output "ERROR: " $msg | Out-File -filepath $global:logfile -Append
}
}
##LogWarning function
##Display provided message as a WARNING in yellow, with WARNING: prefix
##If logging mode is set to true, also write WARNING message to $logfile
Function global:LogWarning($msg){
Write-Host "WARNING: " $msg -ForegroundColor "Yellow"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output "WARNING: " $msg | Out-File -filepath $global:logfile -Append
}
}
##Logging function
##Display provided message as a general information message in cyan
##If logging mode is set to true, also write information message to $logfile
Function global:Logging($msg){
Write-Host $msg -ForegroundColor "Cyan"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output $msg | Out-File -filepath $global:logfile -Append
}
}
from my point of view those functions are not designed as intended, e.g.:
Function global:Logging($msg){
Write-Host $msg -ForegroundColor "Cyan"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output $msg | Out-File -filepath $global:logfile -Append
}
}
PowersShell functions accept named input parameters and are outputting objects in general. In simple words this is the concept. Currently those functions do not return objects they do update/use variables with the scope global. This is a dangerous approach and not needed.
About scopes: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7.2
About functions: https://learn.microsoft.com/en-us/powershell/scripting/learn/ps101/09-functions?view=powershell-7.2
Back to the one example you should do something like this:
Function write-LogFile {
<#
.Description
Enter your description here
.Parameter Message
Enter your description of the parameter here
.Parameter LogToTextFile
Enter the desscription of the parameter here
.Parameter Path
Enter the desscription of the parameter here
.Example
Enter example calls here
#>
param (
[parameter(Mandatory=$true,Position=1)]
[ValidateNotNullOrEmpty()]
[string]$Message,
[parameter(Mandatory=$true,Position=2)]
[switch]$LogToTextFile,
[parameter(Mandatory=$true,Position=3)]
[ValidateNotNullOrEmpty()]
[string]$Path
)
Begin {
}
Process {
try {
Write-Host $msg -ForegroundColor "Cyan"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($LogToTextFile){
Write-Output $msg | Out-File -filepath $path -Append
}
Else {
$msg
}
}
Catch {
write-error $_
}
}
End{
}
}
So I think before you start to merge those function you need first to understand the concept how to write a function. The provided links should help you to find the right path...
I need to go over a list of web services and query them individually and pull the details and store it into the database, I designed the code in the following way
Script 1 connects to the SNOW and pulls in the list of web servcies that i need to connect and loops them through the list and triggers a "Start-ThreadJob" with the service name as an argument
Scipt 2 connects to that specific web service (received as an argument) and connects to it and pull in the data and reports it to the database.
When i run the script 1 manually The script 1 is successfully calling all the required web services and the data is being reported to the database, however when i try to call the script 1 using the windows schdule task, The script 1 executes but the script 2 is not being triggered.
do i need to set
Any specific excution policies
Scope
Any inputs is appreciated, Here is an example for how the script 1 and 2 looks
Script 1
#Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
##############################################################################################################
#Function Name: ErrorHandler
#Inputs: Error Type(Info, Warning or Error), Error String
#Output: None
#Description: If the error type is 'Error' then captures the last known exceptions reported and appends to the file.
# If the error type is something else then the error message is appended to the log file.
##############################################################################################################
Function ErrorHandler ($errType, $ErrorString)
{
$timestamp = Get-Date -Format o
if ($errType -eq 'Error')
{
$errorstr = $timestamp + ' : ' + $errType + ' - ' + $ErrorString + $Error[0].Exception
}
else
{
$errorstr = $timestamp + ' : ' + $errType + ' - ' + $ErrorString
}
$errcounter = 0
$maxerrcounter = 5
do
{
try
{
$errorstr | Out-File $ErrFile -Append
break
}
catch
{
Start-Sleep 10
$errcounter++
}
}while ($errcounter -le $maxerrcounter)
}
##############################################################################################################
#Initialize Variable
##############################################################################################################
#Key file path
$CredFilePath = "E:\WIS\MonitorConfiguration\Schduled\Stage\creds\"
#Error file name
$ErrFile = 'C:\MonitorReportLogs\Sitescope_Config_Pull.log'
$authorization = $null
#SNOW API server fqdn
$SnowFQDNhost = "<Server Name>"
###################################
#Build Snow API authorization header
###################################
$user= Get-Content $CredFilePath'SNOW_User.txt'
$KeyFile = $CredFilePath+'AES.key'
$key = Get-Content $KeyFile
$EncryptedPW = Get-Content $CredFilePath'SNOW_Pass.txt' | ConvertTo-SecureString -Key $key
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($EncryptedPW)
$pass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
$pair = "$($user):$($pass)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$authorization = "Basic $encodedCreds"
#SNOW API authorization Header assignment
$Headers = #{
Authorization = $authorization
}
###################################
#Query SNOW for the list of sitescope instances
###################################
$snowsysurl="https://" + $SnowFQDNhost + "/api/now/table/u_cmdb_relationship_report?sysparm_query=u_service.name=BSM SiteScope - Production&sysparm_fields=u_hardware.fqdn"
try
{
$info=Invoke-WebRequest -Uri $snowsysurl -Headers $Headers
}
catch
{
$errStr = "SNOW Sitescope server list API request for the URL: "+ $snowsysurl +" failed - "
ErrorHandler 'Error' $errStr
}
###################################
#Process each sitescope instance monitor data
###################################
If($info -eq $null){
$errStr = "Unhandled Exception parsing data for " + $SnowFQDNhost + " exiting Script."
ErrorHandler 'Error' $errStr
Exit
}
else
{
#convert the JSON response to PS Custom data object dictionary
$snowContent = $info | ConvertFrom-Json
#Query each sitescope on the list and process the configuration data
$jobCounter = 0
if (($snowContent.result.'u_hardware.fqdn' -ne $null) -and ($snowContent.result.'u_hardware.fqdn' -ne ''))
{
foreach($obj in $snowContent.result.'u_hardware.fqdn')
{
if (($obj -ne $null) -and ($obj -ne ''))
{
Start-ThreadJob -ScriptBlock {param($hostname) E:\WIS\MonitorConfiguration\Schduled\Stage\SIS_Config_DB_Push.ps1 $hostname}`
-ArgumentList ($obj) -ThrottleLimit 10 -Name $obj
$errStr = "Triggered the script for server : "+ $obj
ErrorHandler 'Info' $errStr
}
}
}
else
{
$errStr = "SNOW Sitescope server list API request for the URL: "+ $snowsysurl +" returned 0 records."
ErrorHandler 'Error' $errStr
}
}
###################################
#Garbage Collection
###################################
$info = $null
$snowContent = $null
Script 2: (Limited Version)
#Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
##############################################################################################################
#Function Name: ErrorHandler
#Inputs: Error Type(Info, Warning or Error), Error String
#Output: None
#Description: If the error type is 'Error' then captures the last known exceptions reported and appends to the file.
# If the error type is something else then the error message is appended to the log file.
##############################################################################################################
Function ErrorHandler ($errType, $ErrorString)
{
$timestamp = Get-Date -Format o
if ($errType -eq 'Error')
{
$errorstr = $timestamp + ' : ' + $errType + ' - Server Name: ' + $SiSServerName + ' - ' + $ErrorString + $Error[0].Exception
}
else
{
$errorstr = $timestamp + ' : ' + $errType + ' - Server Name: ' + $SiSServerName + ' - ' + $ErrorString
}
$errcounter = 0
$maxerrcounter = 5
do
{
try
{
$errorstr | Out-File $ErrFile -Append
break
}
catch
{
Start-Sleep 10
$errcounter++
}
}while ($errcounter -le $maxerrcounter)
}
$obj = $args[0]
#Error file name
$ErrFile = 'C:\MonitorReportLogs\Sitescope_Config_Pull.log'
$errStr = "Start processing data for " + $obj
ErrorHandler 'Info' $errStr
I have worked on a powershell script to check if certain important services are running on the server and email me the results. When I test the script and run it in powershell, the emails come through correctly. However when I set up the task on the server and have it run the script, the emails that arrive are now all missing the service names.
Email when the script is run in powershell:
At 12-11-20 11:06, the [service] is ALL GOOD on [server].
Email when the script is run through tasks:
At 12-11-20 11:00, the is on [server].
(services and server names redacted)
Any ideas on what syntax I'm missing that it works in the testing, but not in the actual execution?
My script code (with some details redacted):
$DateTime = Get-Date -Format "MM-dd-yy hh:mm"
#Check if service is running
$servName = "[service I need to check]"
$serv = Get-Service | Where-Object { $_.DisplayName -eq $servName}
$Subject =$Serv.Name + " check on [server]"
If ($serv.Status -ne "Running") {
$Body = "At $DateTime, the " + $serv.DisplayName + " service is " + $serv.Status + " on [server]."
}
Else { $Body = "At $DateTime, the " + $serv.DisplayName + " is ALL GOOD on [server]." }
[location]\EmailAlert.ps1 -Recipient me#work.com -Subject $Subject -Body $Body
$servName = "[another service to check]"
$serv = Get-Service | Where-Object { $_.DisplayName -eq $servName}
$Subject =$Serv.Name + " check on [server]"
If ($serv.Status -ne "Running") {
$Body = "At $DateTime, the " + $serv.DisplayName + " service is " + $serv.Status + " on [server]."
}
Else { $Body = "At $DateTime, the " + $serv.DisplayName + " is ALL GOOD on [server]." }
[location]\EmailAlert.ps1 -Recipient me#work.com -Subject $Subject -Body $Body
My below script will genrate Error log when we have some error in the ZIP file. Zip file should cosnsist of three file if any one is missing the run.bat throw an Error.log file.
I intentionaly made a missing file in ZIP and the script should generat an error.log, but the script is throwing that the Error.log file is blank.
When I tried with break point the script is workking properly and getting mail as there is an error, if I disbale the break point and run it will not send mail. What is wrong in the script. Thank you for your support.
$errorlog = "D:\EmailConnector-Disc Optimus\logs\error.log"
$emailconnecter_log = "D:\EmailConnector-Disc Optimus\logs\connector.log"
#1 print "Running email bat file...... \n";
Write-Host "Running email bat file...... \n"
$prog="cmd.exe"
$params=#('/C','"D:\EmailConnector-Disc Optimus\run.bat"','connector.log')
start-process $prog $params -WorkingDirectory "D:\EmailConnector-Disc Optimus" -RedirectStandardOutput $emailconnecter_log
#$rc = & D:\EmailConnector-Disc Optimus\run.bat > $emailconnecter_log
if (-not $?)
{
Write-Host $rc
$status = "running Daily email bat file failed on $hostname"
Del_Daily
Del_Dat
Send_Mail
exit
}
else
{
Write-Host $rc
Write-Host "Email send sucess fully....."
}
###check for email error log
If (Test-Path $errorlog)
{
$content = Get-Content $errorlog
Write-Host "File is not blank"
Write-Host "File is not blank"
foreach ($line in $content)
{
Write-Host $line
IF ($line | Select-String -Quiet -Pattern '^\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\sERROR')
{
Write-Host $line "Done"
$status = "Daily Email Connector Has ERRORS in the log."
$body = "Check $hostname\logs or $archive_location "
Write-Host $status `n $body
# Send_Mail
$rc = Copy-Item $errorlog $archive_location
$rc = Copy-Item $emailconnecter_log $archive_location
Del_Daily
Del_Dat
exit
}
else{Write-Host "No Error message"}
}
}
else{Write-Host "File is blank"}
We tend to drag and drop messages from outlook into Windows Explorer so I need to rename the default message filename so that the files are searchable/readable from Explorer.
I have managed to put together the following code that almost renames an outlook file on a network folder from the default "Subject.msg" to "To - Subject - ReceivedDate - hhmmss.msg". The only problem is that the rename step does not work as I believe the Outlook process is locking the file. I would appreciate help to avoid the locking and rename the files? Also, I am not sure what happens if there are multiple people in the To list, I would be happy to take the first name in the To list? Here is my effort:
$olMailItemPath = "W:\Marketing\Prospects\Emails\*"
Write-Host $olMailItemPath
$SourceFiles = Get-Item -path $olMailItemPath -include *.msg
$outlook = New-Object -comobject outlook.application
$namespace = $outlook.GetNamespace("MAPI")
function cleanName($aname)
{
$aname = $aname -replace "'"
$aname = $aname -replace ":"
$aname = $aname -replace "#"
$aname = $aname -replace "-"
return ($aname.trim())
}
function cleanSubject($subject)
{
$subject = $subject -replace 'Re:'
$subject = $subject
return (' - ' + $subject.trim() + ' - ')
}
foreach ($msg in $SourceFiles){
$olMailItem = $NameSpace.OpenSharedItem($msg)
$EmailTo = $olMailItem.To
$EmailSubject = $olMailItem.Subject
$DateRecieved = $olMailItem.ReceivedTime
$newfilename = (cleanName($EmailTo)) + (cleanSubject($EmailSubject)) + $DateRecieved.ToString("yyyyMMdd - hhmmss") + ".msg"
# Write-Host "Email Sent To: $EmailTo "
# Write-Host "Subject: $EmailSubject "
# Write-Host "Date Recieved: $DateRecieved"
Write-Host $msg
Write-Host $newfilename
Rename-Item $msg $newfilename
}
p.s. [Inserted # 18 Jun 2013] In answer to Athom, I know Outlook is locking the file as I get the following error:
Rename-Item : The process cannot access the file because it is being used by another process.
At C:\Users\Bertie\Dropbox\Programming\Powershell\Rename Outlook Messages.ps1:41 char:16
+ Rename-Item <<<< -path $msg -newname $newFileName
+ CategoryInfo : WriteError: W:\Marketing\Prospects\Emails\new.msg:String) [Rename-Item], IOException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
However, when I close outlook (which is initiated by the powershell script), I can then run the Rename-Item command and it run's successfully.
How's this?
Essentially the changes I have mades are:
Your renaming loop now throws its output to a hashtable.
Stop-Process kills Outlook.
Another loop then does the renaming.
# Declare the hastable to store the names, then generate the names.
$nameHash = #{}; Foreach ($msg in $SourceFiles){
# Do the Outlook thing
$olMailItem = $NameSpace.OpenSharedItem($msg)
$EmailTo = $olMailItem.To
$EmailSubject = $olMailItem.Subject
$DateRecieved = $olMailItem.ReceivedTime
$newfilename = (cleanName($EmailTo)) + (cleanSubject($EmailSubject)) + $DateRecieved.ToString("yyyyMMdd - hhmmss") + ".msg"
# Write-Host "Email Sent To: $EmailTo "
# Write-Host "Subject: $EmailSubject "
# Write-Host "Date Recieved: $DateRecieved"
# Store the names
$nameHash.Add("$msg","$newfilename")
}
# Kill Outlook.. Then wait....
Stop-Process -Name Outlook -Force
Start-Sleep -m 500 # You might be able to remove this - depends how beefy your CPU is.
# Rename
ForEach ($item in $nameHash.GetEnumerator()) {
# Testing >>-->
echo $item.Name
echo $item.Value
# <--<< Testing
Rename-Item $item.Name $item.Value
}