Incorrect Syntax or misunderstanding powershell Do until loop - variables

I'm currently writing a script to prompt for user input, but want to continue prompting the user until a workstation\IP address or username is entered. This will be implemented into a larger more robust script but i can't get this portion working. Is my syntax off or am I misunderstanding the logic, probably both.
D????-* is our corporate workstation naming standard (ie DOUAW-7811652)
*-admin refers to administrator accounts that are not supported by the larger script
The code looks like this so far:
Function Admin{
Write-host "Admin Accounts are not supported" -ForegroundColor Red
}
Function Workstation{
Write-Host "Workstation $name"
$workstation = $true
}
Function UserName{
Write-Host "User Name $name"
$UserName = $true
}
DO {
$Name = Read-Host "Please enter the User Name or Workstation(or IP)"
switch -Wildcard ($name){
"*-admin" {Admin}
"D????-*"{Workstation}
"*.*.*.*"{Workstation}
Default {UserName}
}
}
until ($UserName -eq "$true" -or $workstation -eq "$true")
Read-Host "Press any key to exit"

(While I might not have written it like this, I just fixed code so you could get out of your infinite loop)
You might want something like
Function Workstation{
Write-Host "Workstation $name"
return $true
}
Function UserName{
Write-Host "User Name $name"
return $true
}
DO {
$Name = Read-Host "Please enter the User Name or Workstation(or IP)"
switch -Wildcard ($name){
"*-admin" {Admin}
"D????-*"{$workstation = Workstation}
"*.*.*.*"{$workstation = Workstation}
Default {$UserName = UserName}
}
} until ($UserName -eq "$true" -or $workstation -eq "$true")

Probably you mean:
until ($UserName -eq $true -or $workstation -eq $true)
(Note no quotes around $true.)

I just wanted to say that this would be a good use of an advanced function. Define your parameters in a way that they do all the validation and enforce requirements. Something like this would get you very close to what you are looking for.
Function Do-Something
{
[CmdletBinding(DefaultParameterSetName="Username")]
param(
[Parameter(
Mandatory = $true,
HelpMessage = "Please enter a username",
Position = 0,
ParameterSetName = "Username",
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[string[]]$Username,
[Parameter(
Mandatory = $true,
HelpMessage = "Please enter a computername",
Position = 0,
ParameterSetName = "Computername",
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[ValidatePattern('D\w\w\w\w-|(\d+\.){3}\d+')]
[string[]]$ComputerName
)
process
{
if($Username){Username}
if($ComputerName){Computername}
}
}
Show-Command Do-Something

Related

Update two different SNMP OID values through Powershell

I'm trying to update info from 4 ups's with two different OID values through powershell. I can update one but when I try to update both values I receive an error. I figured out why it's not updating the values by inserting the values onto a new table. When it inserts/updates the values the script enters both values into the table column instead of having one value for temp and one value for battery. My question is how can I update both values if there is a way. Below is my loop I am running.
# If success go call func SNMP
if($ping_reply.status -eq "Success"){
try {
$frm_snmp = Invoke-SNMPget $ups_ip $oidTemp, $oidBatload "public"
} catch {
Write-Host "$ups_ip SNMP Get error: $_"
Return null
}
# if the data doesn't match record update ups_data
if([String]::IsNullOrWhiteSpace($frm_snmp.Data)){
Write-Host "Given string is NULL"
}else{
if(($ups_temp -and $battery_load -ne $frm_snmp.Data)) {
Write-Output "database update needed"
Write-Output $ups_ip, $ups_upsname $frm_snmp.Data
$new_temp = $frm_snmp.Data
$new_battery_load = $frm_snmp.Data
$update_con = New-Object System.Data.SqlClient.SqlConnection
$update_con.ConnectionString = "connection info"
$update_con.Open()
$SQLstmt = "update ups_data set temp = '$new_temp', batteryload = '$new_battery_load' where ip_address = '$ups_ip'"
$up_cmd = $update_con.CreateCommand()
$up_cmd.CommandText = $SQLstmt
$up_cmd.ExecuteNonQuery()
$update_con.Close()
This is the working code below
# If success go call func SNMP
if($ping_reply.status -eq "Success"){
try {
$frm_snmp = Invoke-SNMPget $ups_ip $oidTemp, $oidBatload "public"
} catch {
Write-Host "$ups_ip SNMP Get error: $_"
Return null
}
# if the data doesn't match record update ups_data
if([String]::IsNullOrWhiteSpace($frm_snmp.Data)){
Write-Host "Given string is NULL"
}else{
if(($ups_temp -and $battery_load -ne $frm_snmp.Data)) {
Write-Output "database update needed"
Write-Output $ups_ip, $ups_upsname $frm_snmp.Data
$new_temp = $frm_snmp.Data
$new_battery_load = $frm_snmp.Data
$update_con = New-Object System.Data.SqlClient.SqlConnection
$update_con.ConnectionString = "connection info"
$update_con.Open()
$SQLstmt = "update ups_data set temp = '$($new_temp[0])', batteryload = '$($new_battery_load[1])' where ip_address = '$ups_ip'"
$up_cmd = $update_con.CreateCommand()
$up_cmd.CommandText = $SQLstmt
$up_cmd.ExecuteNonQuery()
$update_con.Close()

PowerShell RoboCopy path issue

Objective: Robo copy from multiple machines on the network to a network share using variables for both the machine name and the currently logged on user.
What I have: txt file with a list of computernames.
Issue: I cannot get the foreach to work with the .split("\")[1] I use on the username variable to remove the domain prefix so I can use the output from that in the robocopy path
something like
robocopy "\\$computername\c$\documents and settings\$username\backup" "\\networkshare\backup\$username\backup"
gives me the error
You cannot call a method on a null-valued expression.
At C:\Scripts\Test\backup.ps1:13 char:2
Here's what I have so far. Can somebody help please?
function Get-LoggedIn {
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[string[]]$computername
)
foreach ($pc in $computername){
$logged_in = (gwmi win32_computersystem -COMPUTER $pc).username
$name = $logged_in.split("\")[1]
"{1}" -f $pc,$name
}
}
$computers = Get-Content "C:\Scripts\testcomputers.txt"
foreach ($computer in $computers) {
$users = Get-LoggedIn $computer
}
$SourceFolder = "\\$computer\c$\users\$users\desktop"
$DestinationFolder = "\\networkshare\backups\$users\backup\desktop"
$Logfile = "\\networkshare\backups\$users\backup\backuplog.txt"
Robocopy $SourceFolder $DestinationFolder /E /R:1 /W:1 /LOG:$Logfile
I see multiple errors here. You're not running the copy commands inside the foreach-loop. The username property recieved from WMI can often be in the following format:
domain\computer\username (or computer\domain\username, unsure since I'm on non-domain workstation now)
Anyways, the username is always the last part, so get it by using the index [-1] instead.
Updated script (with indents!):
function Get-LoggedIn {
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[string[]]$computername
)
foreach ($pc in $computername){
$logged_in = (gwmi win32_computersystem -COMPUTER $pc).username
$name = $logged_in.split("\")[-1]
"{1}" -f $pc,$name
}
}
$computers = Get-Content "C:\Scripts\testcomputers.txt"
foreach ($computer in $computers) {
$users = Get-LoggedIn $computer
$SourceFolder = "\\$computer\c$\users\$users\desktop"
$DestinationFolder = "\\networkshare\backups\$users\backup\desktop"
$Logfile = "\\networkshare\backups\$users\backup\backuplog.txt"
& Robocopy $SourceFolder $DestinationFolder /E /R:1 /W:1 /LOG:$Logfile
}

Scripting out individual objects from SQL using SMO

For my job I often have to script out a table with all its keys, constraints and Triggers (basically a full script to recreate the table) from a Microsoft SQL 2008 server.I also have to do this for procedures and triggers.
What I do now is open SSMS right click the object and select script to and select to script it to a file. So if I have 3 procedures to do and 10 tables and 1 trigger I end up doing this 14 times .
What I would like is a powershell script that I could feed a list of objects to and then it would go and use SMO to script each on out to an individual file.
Thanks for the help
Here is a PowerShell function I use whenever I have to script a database. It should be easy to modify just to scripts the objects you need.
function SQL-Script-Database
{
<#
.SYNOPSIS
Script all database objects for the given database.
.DESCRIPTION
This function scripts all database objects (i.e.: tables, views, stored
procedures, and user defined functions) for the specified database on the
the given server\instance. It creates a subdirectory per object type under
the path specified.
.PARAMETER savePath
The root path where to save object definitions.
.PARAMETER database
The database to script (default = $global:DatabaseName)
.PARAMETER DatabaseServer
The database server to be used (default: $global:DatabaseServer).
.PARAMETER InstanceName
The instance name to be used (default: $global:InstanceName).
.EXAMPLE
SQL-Script-Database c:\temp AOIDB
#>
param (
[parameter(Mandatory = $true)][string] $savePath,
[parameter(Mandatory = $false)][string] $database = $global:DatabaseName,
[parameter(Mandatory = $false)][string] $DatabaseServer = $global:DatabaseServer,
[parameter(Mandatory = $false)][string] $InstanceName = $global:InstanceName
)
try
{
if (!$DatabaseServer -or !$InstanceName)
{ throw "`$DatabaseServer or `$InstanceName variable is not properly initialized" }
$ServerInstance = SQL-Get-Server-Instance $DatabaseServer $InstanceName
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
$s = New-Object Microsoft.SqlServer.Management.Smo.Server($ServerInstance)
$db = $s.databases[$database]
$objects = $db.Tables
$objects += $db.Views
$objects += $db.StoredProcedures
$objects += $db.UserDefinedFunctions
$scripter = New-Object ('Microsoft.SqlServer.Management.Smo.Scripter') ($s)
$scripter.Options.AnsiFile = $true
$scripter.Options.IncludeHeaders = $false
$scripter.Options.ScriptOwner = $false
$scripter.Options.AppendToFile = $false
$scripter.Options.AllowSystemobjects = $false
$scripter.Options.ScriptDrops = $false
$scripter.Options.WithDependencies = $false
$scripter.Options.SchemaQualify = $false
$scripter.Options.SchemaQualifyForeignKeysReferences = $false
$scripter.Options.ScriptBatchTerminator = $false
$scripter.Options.Indexes = $true
$scripter.Options.ClusteredIndexes = $true
$scripter.Options.NonClusteredIndexes = $true
$scripter.Options.NoCollation = $true
$scripter.Options.DriAll = $true
$scripter.Options.DriIncludeSystemNames = $false
$scripter.Options.ToFileOnly = $true
$scripter.Options.Permissions = $true
foreach ($o in $objects | where {!($_.IsSystemObject)})
{
$typeFolder=$o.GetType().Name
if (!(Test-Path -Path "$savepath\$typeFolder"))
{ New-Item -Type Directory -name "$typeFolder"-path "$savePath" | Out-Null }
$file = $o -replace "\[|\]"
$file = $file.Replace("dbo.", "")
$scripter.Options.FileName = "$savePath\$typeFolder\$file.sql"
$scripter.Script($o)
}
}
catch
{
Util-Log-Error "`t`t$($MyInvocation.InvocationName): $_"
}
}
Here's a script to backup an individual object. Simply pass the object name to the function:
http://sev17.com/2012/04/backup-database-object/

Retrieve calendar items (Outlook API, WebDAV) displaying strange behaviour

We are writing an MS Outlook plugin. To satisfy our business-logic, it should check all appointments between some dates. We are experiencing several problems with retrieving all items from calendars. We tried two options:
Outlook API. We use the standard logic that is described in MSDN - sort items by [Start], set IncludeRecurrences to True and run the Find\Restrict query over calendar items like here. It works fine in our test environment. However, in our customer's environment: For recurring appointments, start and end dates are set to the corresponding dates of a 'master appointment.' For example, in some room's calendar we have a weekly appointment that was created in January, and if we try to find all items in August, we get among others four items of this recurring appointment, but their start and end dates are set to January. But Outlook displays correct dates in the same calendar...
Very bad, but we still have WebDAV! We write a simple test application and try to query all items from the calendar using WebDAV. Of course, we didn't reinvent the wheel and just pasted the code from documentation. The previous problem is solved, but the next one arises: It doesn't return recurring items that were created more than approximately six months ago. I Haven't a clue - there are no parameters restricting 'old' items!
What is wrong? Are we missing something important?
Technical details: Exchange 2003, Outlook 2003-2010. Frankly speaking, the first error disappears if we turn on Cached Exchange Mode, but we can't do that.
var nameSpace = application.GetNamespace("MAPI");
var recepient = nameSpace.CreateRecipient(roomEMail);
recepient.Resolve();
var calendar = nameSpace.GetSharedDefaultFolder(recepient, OlDefaultFolders.olFolderCalendar);
var filter = string.Format("[Start]<'{1}' AND [End]>'{0}'",
dateFrom.ToString("dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture), dateTo.ToString("dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture)
);
var allItems = calendar.Items;
allItems.Sort("[Start]");
allItems.IncludeRecurrences = true;
var _item = allItems.Find(filter);
while (_item != null) {
AppointmentItem item = _item as AppointmentItem;
if (item != null) {
if (item.Subject != "some const")
&& (item.ResponseStatus != OlResponseStatus.olResponseDeclined)
&& (item.MeetingStatus != OlMeetingStatus.olMeetingReceivedAndCanceled
&& item.MeetingStatus != OlMeetingStatus.olMeetingCanceled))
{
/* Here we copy item to our internal class.
* We need: Subject, Start, End, Organizer, Recipients, MeetingStatus,
* AllDayEvent, IsRecurring, RecurrentState, ResponseStatus,
* GlobalAppointmentID */
}
}
_item = allItems.FindNext();
}
UPDATE 1:
Additional research using OutlookSpy shows that the problem is not in our code - the Start\End dates are incorrect inside the API when Cached Exchange Mode is off. But Outlook developers were aware of it, and they somehow display correct dates in calendars! Does anyone know how?
UPDATE 2:
Answer from Outlook Support Escalation Engineer:
Based on this, I can confirm that this is a problem in our product.
Possible cause:
Sort after setting IncludeRecurrences.
Here is my code of a PowerShell module that retrieves Outlook items between two dates.
And a little applet to check for changes and send an email including the agenda updates, which comes handy when you don't have mobile access to the Exchange.
Path: Documents\WindowsPowerShell\Modules\Outlook\expcal.ps1
Function Get-OutlookCalendar
{
<#
.Synopsis
This function returns appointment items from default Outlook profile
.Description
This function returns appointment items from the default Outlook profile. It uses the Outlook interop assembly to use the olFolderCalendar enumeration.
It creates a custom object consisting of Subject, Start, Duration, Location
for each appointment item.
.Example
Get-OutlookCalendar |
where-object { $_.start -gt [datetime]"5/10/2011" -AND $_.start -lt `
[datetime]"5/17/2011" } | sort-object Duration
Displays subject, start, duration and location for all appointments that
occur between 5/10/11 and 5/17/11 and sorts by duration of the appointment.
The sort is the shortest appointment on top.
.Notes
NAME: Get-OutlookCalendar
AUTHOR: ed wilson, msft
LASTEDIT: 05/10/2011 08:36:42
KEYWORDS: Microsoft Outlook, Office
HSG: HSG-05-24-2011
.Link
Http://www.ScriptingGuys.com/blog
#Requires -Version 2.0
#>
echo Starting... Initialize variables
Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
$olCalendarDetail = "Microsoft.Office.Interop.Outlook.OlCalendarDetail" -as [type]
echo ... Getting ref to Outlook and Calendar ...
$outlook = new-object -comobject outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
$folder = $namespace.getDefaultFolder($olFolders::olFolderCalendar)
echo ... Calculating dates ...
$now = Get-Date -Hour 0 -Minute 00 -Second 00
echo From $a To $b
echo ... Getting appointments ...
$Appointments = $folder.Items
$Appointments.IncludeRecurrences = $true
$Appointments.Sort("[Start]")
echo ... Setting file names ...
$oldfile = "$env:USERPROFILE\outlook-calendar.bak"
echo oldfile: $oldfile
$newfile = "$env:USERPROFILE\outlook-calendar.txt"
echo newfile: $newfile
$calfile = "$env:USERPROFILE\outlook-calendar.ics"
echo calfile: $calfile
echo ... Exporting calendar to $calfile ...
$calendarSharing = $folder.GetCalendarExporter()
$calendarSharing.CalendarDetail = $olCalendarDetail::olFullDetails
$calendarSharing.IncludeWholeCalendar = $false
$calendarSharing.IncludeAttachments = $false
$calendarSharing.IncludePrivateDetails = $true
$calendarSharing.RestrictToWorkingHours = $false
$calendarSharing.StartDate = $now.AddDays(-30)
$calendarSharing.EndDate = $now.AddDays(30)
echo $calendarSharing
$calendarSharing.SaveAsICal($calfile)
echo ... Backing up $newfile into $oldfile ...
if (!(Test-Path $newfile)) {
echo "" |Out-File $newfile
}
# Backup old export into $oldfile
if (Test-Path $oldfile) {
echo "Deleting old backup file $oldfile"
del $oldfile
}
echo " ... moving $newfile into $oldfile ... "
move $newfile $oldfile
echo "... Generating text report to file $newfile ..."
$Appointments | Where-object { $_.start -gt $now -AND $_.start -lt $now.AddDays(+7) } |
Select-Object -Property Subject, Start, Duration, Location, IsRecurring, RecurrenceState |
Sort-object Start |
Out-File $newfile -Width 100
echo "... Comparing with previous export for changes ..."
$oldsize = (Get-Item $oldfile).length
$newsize = (Get-Item $newfile).length
if ($oldsize -ne $newsize ) {
echo "!!! Detected calendar change. Sending email..."
$mail = $outlook.CreateItem(0)
#2 = high importance email header
$mail.importance = 2
$mail.subject = $env:computername + “ Outlook Calendar“
$mail.Attachments.Add($newfile)
$mail.Attachments.Add($calfile)
$text = Get-Content $newfile | Out-String
$mail.body = “See attached file...“ + $text
#for multiple email, use semi-colon ; to separate
$mail.To = “your-email#your-mail-domain.com“
$mail.Send()
}
else {
echo "No changes detected in Calendar!"
}
} #end function Get-OutlookCalendar
Function Get-OutlookCalendarTest
{
echo starting...
Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
$outlook = new-object -comobject outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
$folder = $namespace.getDefaultFolder($olFolders::olFolderCalendar)
$a = Get-Date -Hour 0 -Minute 00 -Second 00
$b = (Get-Date -Hour 0 -Minute 00 -Second 00).AddDays(7)
echo From $a To $b
$Appointments = $folder.Items
$Appointments.IncludeRecurrences = $true
$Appointments.Sort("[Start]")
$Appointments | Where-object { $_.start -gt $a -AND $_.start -lt $b } | Select-Object -Property IsRecurring, RecurrenceState, Subject, Start, Location
} #end function Get-OutlookCalendarTest
This is the code to invoke the PowerShell function in the module:
Path: Documents\WindowsPowerShell\mono.ps1
Import-Module -Name Outlook\expcal.psm1 -Force
$i=0
#infinite loop for calling connect function
while(1)
{
$i = $i +1
Write-Output "Running task Get-OutlookCalendar ($i)"
Get-OutlookCalendar
start-sleep -seconds 300
}
To run the PowerShell script, use powershell.exe. To run this on startup, a shortcut on "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\":
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "C:\Users\%USERNAME%\Documents\WindowsPowerShell\mono.ps1"

Powershell if statements not executing

I have the follow snippet a powershell script and the nested if statements are not being executed. Can you tell me why ? I've recently got back into the programming game so its probably something simple
$false.
$IgnoreThisLine = $false
$tmpString = "(DATUM CTR OF BLADE)"
$EndOfDescription = $null
$cncPrograms ="(165756 SIDE 1)"
if ($IgnoreThisLine = $false ) {
If ($tmpString -match '\(') {
$EndOfDescription = $false
$cncPrograms = $tmpString
}
else {
$EndOfDescription = $true
}
}
if ($IgnoreThisLine -eq $false)
"-eq" not "=" to test for equality with PowerShell.