Labels

Showing posts with label PowerShell. Show all posts
Showing posts with label PowerShell. Show all posts

Saturday, June 20, 2020

Finding where a user is logging on from

For years I’ve been using a doskey macro I created to Find a User.

In an enterprise environment, the logic is:

  • Every normal user account has their home server mapped automatically, establishing a persistent SMB session with the home server from their workstation 
  • Find the home server and query it to find the where the user is connecting from 
  • Resolve the address and report who is connecting from where.

A few limitations:

  1. This will only work if the home server is a Windows box 
  2. You will need permissions to query win32_serversession of the home remotely (typically admin) 
  3. If the person is connecting over Citrix or DirectAccess or another jump box, it will resolve to that source, instead of (or sometimes as well as) a workstation.

A quick PowerShell equivalent (with zero error checking):

function Find-User ($username) {
  $homeserver = ((get-aduser -id $username -prop homedirectory).Homedirectory -split "\\")[2]
  $query = "SELECT UserName,ComputerName,ActiveTime,IdleTime from win32_serversession WHERE UserName like '$username'"
  $results = Get-WmiObject -Namespace root\cimv2 -computer $homeServer -Query $query | Select UserName,ComputerName,ActiveTime,IdleTime
  foreach ($result in $results) {
    $hostname = ""
    $hostname = [System.net.Dns]::GetHostEntry($result.ComputerName).hostname
    $result | Add-Member -Type NoteProperty -Name HostName -Value $hostname -force
    $result | Add-Member -Type NoteProperty -Name HomeServer -Value $homeServer -force
  }
  $results
}

# Find one or more users
$users = "user1", "user2", "user3"
$users | % {Find-User $_} | ft -wrap -auto

# Find the members of a group
get-adgroupmember -id SG-Group1 | % {Find-User $_.samaccountname} | ft -wrap -auto

The original (and still the best) doskey macro:

FU=for %g in ($1 $2 $3 $4 $5 $6 $7 $8 $9) do @for /f "tokens=2 delims=\" %i in ('"dsquery user -samid %g | dsget user -hmdir | find /i "%g""') do @for /f "skip=1 tokens=1-3" %m in ('"wmic /node:"%i" path win32_serversession WHERE "UserName Like '%g'" Get ComputerName,ActiveTime,IdleTime"') do @for /f "tokens=2" %q in ('"ping -a %n -n 1 | find /i "pinging""') do @echo %q %g %n %i %m %o

Create the macro above with doskey:

doskey /listsize=1000 /macrofile=c:\util\macros.txt
FU user1


Wayne's World of IT (WWoIT). 


Read more!

Wednesday, June 17, 2020

Useful PowerShell command-lines #2

The 185 commands below are a random list of PowerShell one-liners I've taken note of over the years. Some of these commands are approaching 10 years old, so while all of them probably still work there are most likely better ways of achieving the same outcome with current versions of PowerShell and the underlying Operating System.

Each command-line can be copied and pasted at a PowerShell command prompt, or you can use the commands as part of a PS1 script file if you prefer.



Split a string on spaces, removing empty entries
$line.Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)

Measure how long a commands takes to execute
measure-command -expression {}

List the processes running on a remote machine
$process = [System.Diagnostics.Process]; $process::GetProcesses($server)

Get a process by ID running on a remote machine
$process = [System.Diagnostics.Process]; $proc = $process::GetProcessById(5716,$server)

Set the priority of a process to above normal
$proc.set_PriorityClass([System.Diagnostics.ProcessPriorityClass]::AboveNormal)

Create a new profile with the default profile variable
new-item -type file -force $profile

Create an empty object with the specified properties
$test = "" | Select-Object Name,Speed

Convert a SID to NT account name
$trustee = new-object System.Security.Principal.SecurityIdentifier("S-1-5-21-1234530602-3734247491-3823728601-63426"); $trustee.Translate([System.Security.Principal.NTAccount])

Delete the master account SID attribute from an AD object
$user = [ADSI]$ADsPath ; $user.putex(1,"msExchMasterAccountSid",$null)

Set the execution policy to allow local scripts to run unsigned
Set-ExecutionPolicy RemoteSigned

Set process affinity
$calcSet = Get-Process -ProcessName "calc" ; foreach ($calc in $calcSet) {$calc.ProcessorAffinity=0x1}

List the values of an enumeration
[enum]::GetValues([VMware.VimAutomation.Types.NamingScheme])

Use the WinNT provider to check administrative membership for a remote computer
[ADSI]"WinNT://" + $computerName + "/Administrators,group"; $members = $adminGroup.psbase.invoke("Members") | %{$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)} | sort-object

Export the key/value pairs of a hash table to csv
$test.GetEnumerator() | export-csv -path c:\temp\hashtable.csv

Return the date format using the get-date cmdlet
Get-Date -format "dd/MM/yyyy HH:mm:ss"

Create an associative array / hash table /
$test = @{a=1; b=2}

Sort a hashtable associative array by name or value
$results = @{a=1; b=2;c=0}; $results.GetEnumerator() | sort-object -property Name

Sleep or pause for 10 seconds
Start-Sleep -seconds 10

Find the last win32 exit code (errorlevel)
$lastexitcode

Find the name of the currently running script
$MyInvocation.MyCommand.path

Convert a string into datetime type using the current culture
$test = "17/03/2010 10:00:00 AM"; [datetime]::Parse($test, [System.Threading.Thread]::CurrentThread.CurrentCulture)

Run an infinite loop
for (;;) {write-output "loop"}

Process a list of files, extracting the first group of a repeating set of data
$files = get-item -path .\*; foreach ($file in $files) {$sandata = get-content -path $file; $count=0; foreach ($line in $sandata) {$csv = $line.split(","); if ($csv[0] -like '*textfilter*') {$count+=1}; if ($count -le 1) {if ($csv[0] -notlike '*Object*') {$line | out-file -file c:\temp\DailySANExport.csv -encoding ascii -append}}}}

Query Citrix XenApp server session information
Get-Wmiobject -namespace root\Citrix -class MetaFrame_Session -computer server01 | format-table -wrap -autosize

Query Citrix XenApp server load information
Get-Wmiobject -namespace root\Citrix -class MetaFrame_Server_LoadLevel -computer server01,server02,server03| format-table -wrap -autosize -prop ServerName,LoadLevel

Find the size of a folder and contents (including subdirectories)
Get-ChildItem $dirPath -recurse | Measure-Object -property length -sum

Round a number down
[math]::floor(100.9)

Find the last bootup time of a Windows OS
$lastBootTime = Get-WmiObject win32_operatingsystem -computer server01 -prop LastBootUpTime

Find the uptime of a machine from WMI, converted from CIM datetime to timespan
$computer = 'server01'; $lastBootTime = Get-WmiObject win32_operatingsystem -computer $computer -prop LastBootUpTime; $wbemDateTime = New-Object -ComObject WbemScripting.SWbemDateTime; $wbemDateTime.value = $lastboottime.LastBootUpTime; $lastBoot = $wbemDateTime.GetVarDate(); $now = Get-Date; $uptime = $now - $lastBoot; $uptime

Select a calculated property using a friendly name
Get-WmiObject -class win32_process | Select-Object -prop Name, @{Name="Owner";Expression ={($_.getowner().domain + "\" + $_.getowner().user)}} | format-table -wrap -autosize

List Processes and their owner
Get-WmiObject -class win32_process | Select-Object -prop Name, @{Name="Owner";Expression ={($_.getowner().domain + "\" + $_.getowner().user)}} | format-table -wrap -autosize

Create a PSObject to store name/value note pairs
$output = new-object PSObject; add-member -membertype NoteProperty -inputObject $output -name "Test" -value "value"

Start a command shell with elevated (UAC) privileges
$psi = new-object System.Diagnostics.ProcessStartInfo "cmd.exe"; $psi.Verb = "runas"; [System.Diagnostics.Process]::Start($psi)

Mail-enable an AD contact in an Exchange 2007 environment
get-mailcontact "CN=user1,DC=domain,DC=local" | set-mailcontact

Query the amount of free space available for 2008 R2 disk shrinking
diskpart shrink querymax

Find the local PowerShell version
$PSVersionTable

Read a file, sort it and then return only unique entries
gc $filename | sort | get-unique > $newfileName

Find unique strings filtered from an input file
find /i '"driverName"' PrinterDrivers_20110708.txt | sort | get-unique > c:\temp\PrinterDrivers.txt

Create a security identifier for a well-known security principal
$self = new-object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::SelfSid, $null)

Convert the Exchange 2007 string into readable format (EXCHANGE12ROCKS)
$out = ""; foreach ($char in ([char[]]"FYDIBOHF23SPDLT")) {$out += ([char]([int]$char-1))}; $out

WMI query to find properties is or is not NULL
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -computer "server01" -Query "SELECT MailboxDisplayName,TotalItems,Size from Exchange_Mailbox WHERE MailboxDisplayName='Cartelier, Robbie' AND DateDiscoveredAbsentInDS is null"

Query disk information from a remote server using WMI
$disks = Get-WmiObject -Namespace root\cimv2 -ComputerName server01 -Query "SELECT * from Win32_LogicalDisk WHERE FileSystem='NTFS'"

Find services filtered by string that are running and stop them
get-service | where {$_.displayName -like '*time*' -and $_.status -eq 'Running'} | stop-service -force

Find and delete a local profile from a remote computer
$user = "domain\account"; $computer = "server01"; $trustee = new-object System.Security.Principal.NTAccount($user); $sid = $trustee.Translate([System.Security.Principal.SecurityIdentifier]).value; get-wmiobject -computer $computer -Query "SELECT * from Win32_UserProfile Where SID = '$sid'"; $profile.delete()

Create an array with a single member
$SingleArray = ,1

Store the results of an expression in an array
$test = @(get-service )

Find disk drive statistics from a number of remote computers
$servers = get-content -path servers.txt; $diskStats = $null; foreach ($server in $servers) { $diskStats += Get-Wmiobject -namespace root\cimv2 -computer $server -Query "SELECT SystemName,Name,Size,FreeSpace,VolumeName FROM Win32_LogicalDisk WHERE Size > 0 AND FileSystem='NTFS'" -ErrorAction SilentlyContinue}; $diskstats | select-object SystemName,Name,Size,FreeSpace,@{N="Used";E={$_.Size-$_.FreeSpace}},VolumeName,@{N="SizeGB";E={[math]::round($_.Size/1024/1024/1024)}},@{N="FreeGB";E={[math]::round($_.FreeSpace/1024/1024/1024)}},@{N="UsedGB";E={[math]::round(($_.Size-$_.FreeSpace)/1024/1024/1024)}} | sort -prop SystemName,Name | export-csv -path servers_diskstats.csv

Find USB devices attached to a number of remote computers
$servers = get-content -path servers.txt; foreach ($server in $servers) {[System.Object[]]$USBDevices += Get-Wmiobject -namespace root\cimv2 -computer $server -Query "SELECT * FROM Win32_DiskDrive WHERE InterfaceType = 'USB'" -ErrorAction SilentlyContinue}; $USBDevices| select __server, Caption, @{N="Size (GB)";E={[math]::round($_.Size/1000/1000/1000)}} | ft -wrap -autosize

Use the split operator to split on multiple characters
$user.proxyAddresses -split ";;"

Find the CA eTrust signature version from the agent.xml file
$ver = select-xml -path c:\temp\agent.xml -xpath '//thisProduct[@Name="eTrust Integrated Threat Manager"]/*/*[@Name="Anti-Malware Signatures"]'; $ver.node.version

Test a host connection with ping
if (test-connection -computer "server01" -count 1 -quiet) {write-host "test"}

Read and process an XML file on a list of servers, returning some attributes
$servers = get-content -path servers.txt; foreach ($server in $servers) {  if (test-connection -computer $server -count 1 -quiet) {    $path = '\\' + $server + '\c$\Program Files\CA\SharedComponents\Agent\Agent.xml';     if (test-path -path $path) {      $ver = select-xml -path $path -xpath '//thisProduct[@Name="eTrust Integrated Threat Manager"]/*/*[@Name="Anti-Malware Signatures"]';      if ($ver) {write-output ($server + "," + $ver.node.version.major + '.' + $ver.node.version.minor + '.' + $ver.node.version.build + '.' + $ver.node.version.revision + "," + $ver.node.LastUpdateTime)}     } else {      Write-Output ($server + "," + "agent.xml not found")    }  }}

Find the uptime from one or more remote machines
$servers = get-content -path servers.txt; foreach ($computer in $servers) {   if (test-connection -computer $computer -count 1 -quiet) {     $lastBootTime = Get-WmiObject win32_operatingsystem -computer $computer -prop LastBootUpTime;     $wbemDateTime = New-Object -ComObject WbemScripting.SWbemDateTime;     $wbemDateTime.value = $lastboottime.LastBootUpTime;     $lastBoot = $wbemDateTime.GetVarDate();     $now = Get-Date; $uptime = $now - $lastBoot;     Write-Host ($computer + "," + $uptime.days + "," + $lastBoot.ToString("dd/MM/yyyy"));   } }

Set the window title of a PowerShell window
$host.UI.rawui.windowtitle = "test"

Kill a remote process with WMI
([WMI]"\\server01\root\cimv2:Win32_Process.Handle='2564'").Terminate()

Convert a SWBEM datetime yyyymmhhdd time to standard datetime
$datetime = [System.Management.ManagementDateTimeConverter]::ToDateTime($installDate)

Find DNS scavenging events from a 2008 R2 server
$DNS = Get-Wmiobject -namespace root\cimv2 -computer "server01" -Query "SELECT * FROM Win32_NTLogEvent WHERE SourceName='Microsoft-Windows-DNS-Server-Service' AND LogFile='DNS Server' AND EventCode=2501" -ErrorAction SilentlyContinue; Write-Host "Time Generated,Visited Zones,Visited Nodes,Scavenged Nodes,Scavenged Records,Elapsed Seconds,Run again in hours" ; foreach ($scavenge in $dns) {write-output ([System.Management.ManagementDateTimeConverter]::ToDateTime($scavenge.timeGenerated).tostring() + "," + [string]::join(",",$scavenge.insertionstrings))}

Check whether the windows Search Service file services role is installed
wmic /node:server01 path Win32_ServerFeature where "ID=107"

Query remote event logs for DFS initial sync replication log entries
get-eventlog -logname 'DFS Replication' -computer server01 -after "15/01/2012 8:00:00" | where {$_.eventID -eq 4104}

Query local network connections (netstat)
[net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpConnections()

Find when a number of machines had their NIC disconnected
$vms = get-content -path servers.txt; $events = foreach ($server in $vms) {get-eventlog -logname 'System' -computer $server -after "31/01/2012 17:00:00" | where {$_.eventID -eq 4201 -or $_.eventID -eq 4202}}; $events | select MachineName,EventID,TimeGenerated,Source,{$_.ReplacementStrings} | export-csv -path c:\temp\VM_NetworkDisconnectedEvents.csv

Enumerate arrays and output their contents to CSV
$events | select MachineName,EventID,TimeGenerated,Source,{$_.ReplacementStrings} | export-csv -path c:\temp\VM_NetworkDisconnectedEvents.csv

Search through text logs looking for a string
select-string -pattern [email protected] -path .\ExchangeLogs\*.* -SimpleMatch

Compare two sets of objects to identify differences
compare-object -referenceobject $processes_before -differenceobject $processes_after

Find the default domain password policy
Get-ADDefaultDomainPasswordPolicy

Find the available PowerShell modules
get-module -listAvailable

Find the PowerShell modules that are installed in the current session
get-module

Find the commands available in a specific PowerShell module
get-command -module GroupPolicy

Find IPMI WMI recent SEL event information from a number of servers
$servers = get-content -path servers.txt; $IPMIStats = $null ;foreach ($server in $servers) {  $IPMIStats += Get-Wmiobject -namespace root\hardware -computer $server -Query "SELECT __server,MessageTimestamp,Description FROM LogRecord WHERE MessageTimestamp > '20120201000000.000000+600'" -ErrorAction SilentlyContinue}; $IPMIstats | select-object __server,MessageTimestamp,Description | sort -prop __server | export-csv -path c:\temp\SEL_20120225.csv

Read and decode the DACL stored in a REG_BINARY object in the registry
$reg = get-itemproperty "HKLM:\System\CurrentControlSet\Services\LanmanServer\DefaultSecurity"; $acl = New-Object Security.AccessControl.RawSecurityDescriptor($($reg.SrvsvcSharePrintInfo), 0); $acl.DiscretionaryAcl; # see http://msdn.microsoft.com/en-us/library/cc244650(PROT.10).aspx for access mask

Convert REG_BINARY filetime stored in reversed byte/word format to date/time
[datetime]::FromFileTime([Convert]::ToInt64("01CD098EBB74AE65", 16))

List the PowerShell profile script path properties
$profile | select *

Read from remote event logs with PowerShell 2.0 or later
Get-WinEvent

Get the event log provider names for the specified log
$log = get-winevent -listlog Security | select providernames; $log.providernames

Find the EventID and descriptions from the specified event log provider
(get-winevent -listprovider 'Microsoft-Windows-Security-Auditing').events | ft ID,Description -autosize

Reverse an array
[array]::Reverse($array)

Join an array and output as a string with the specified delimiter
("test1", "test2") -join ";"

Add a UPN suffix to the local forest
get-adforest -current localcomputer | set-adforest -upnsuffixes @{Add="newsuffix.com"}

Modify the UPN for a user
get-aduser -id user01 | set-aduser -UserPrincipalName [email protected]

Extract error information
$error[0].Exception | select * ; $error[0].Exception.InnerException | select *

Export a single property from multiple objects to file
$objects | select -prop prop01 | export-csv -notype -path c:\temp\output.txt -encoding ascii

Export server shares to a csv file
$outputfile = "c:\temp\server01_shares_" + [DateTime]::Now.ToString("yyyyMMddhhmmss") + ".csv"; Get-WmiObject win32_share -computer server01 | select Name,Path,Description,Caption | export-csv -path $outputFile; $outputFile

Check each line of one file for a match in a second file
$inputLines = get-content -path c:\temp\File01.txt; foreach ($line in $inputLines) {$match = select-string -pattern $line -path File02.txt -SimpleMatch; if (!($match)) {$member}}

Join a file in blocks of two lines
$text = get-content -path File.txt; $results = for($i=0; $i -le $text.length; $i = $i+2){Write-Output ($text[$i] + "; " + $text[$i+1])}

Convert a unicode hex-string to human readable string
$converted = for ($i=0; $i -le $string.length-1; $i = $i+4) {write-output ([CHAR][BYTE]([CONVERT]::toint16($string.substring($i, 2),16)))}; [string]::join("",$converted)

Find the snap-ins currently registered
get-PSsnapin -registered

Run FIM 2010 R2 Microsoft Best-practices Configuration Analyser
Import-module "C:\Program Files\Microsoft Baseline Configuration Analyzer 2\Modules\BaselineConfigurationAnalyzer\BaselineConfigurationAnalyzer"; Invoke-MBCAModel -ModelId FIMBPA -SubModel FIMService -computer fimservice

Binary OR of useraccountcontrol to see if an account is enabled/disabled
(514 -bor 2) -eq 514

Convert a date to filetime (64-bit 100-nanosecond since midnight, 01/01/1601)
$date = [datetime]"24 December 2012"; $date.tofiletime()

Regular expression for numbers with spaces or brackets
'^[\d() -]+$'

Remove brackets and spaces from a string
$test -replace('\(|\)|\s','')

Use the Modulus operator as a way of reporting status in a loop every x
$progress = $count % 1000; if ($progress -eq 0) { Write-Output $count}          # Report every 1000

Find the current running username in domain\user format
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name

Find the current running username
$env:username

Break a loop if keyboard input is detected
if($host.UI.RawUI.KeyAvailable) {break;}

Loop infintely until the 'Q' key is pressed
$Qkey = 81; for (;;) {  start-sleep 5; if($host.UI.RawUI.KeyAvailable) { $key = $host.ui.RawUI.ReadKey("NoEcho,IncludeKeyUp")  ; if ($key.VirtualKeyCode -eq $Qkey) ;  { break; } }  Write-Output "$(get-date)" }

Install IIS on 2008 onwards
Install-WindowsFeature -Name Web-Server -IncludeAllSubFeature

Find installed hotfixes and installation date
$hotfixes = Get-WmiObject -Namespace root\cimv2 -computer Computer -Query "Select HotfixID,ServicePackInEffect,InstallDate,InstalledBy,InstalledOn from win32_quickfixengineering"

Write a System.Byte[] array to a binary file
set-content -value $byteArray -encoding byte -path c:\temp\image.bmp

Convert decimal to hex
'{0:x}' -f 15

Rename an Active Directory object (caters for naming attribute renames)
rename-adobject -id "CN=User1,OU=Users,DC=domain,DC=local" -newname user2 -server 192.168.10.10

Convert yyyymmdd to [datetime]
[datetime]::ParseExact("20130913", "yyyymmdd",  [Globalization.CultureInfo]::InvariantCulture)

Match an array of objects against a string using regular expressions
$mailbox = $mailboxes -match "MARTIN Wayne"

Create a generic log file name based on the script name and today's date
$logFile = ".\" + ($MyInvocation.MyCommand.Name.split("."))[0] + "_" + [DateTime]::Now.ToString("yyyyMMdd") + ".log"

Split a string (eg distinguishedName) containing escaped commas
$dn -split "(?<![\\]),"

List the event log providers on a remote computer
get-winevent -computer server01 -listprovider *

Append to the System path environment variable
$path = [environment]::GetEnvironmentVariable("Path","Machine"); [Environment]::SetEnvironmentVariable("Path", "$path;c:\util", "Machine")

Use AD cmdlets to change the samaccountname of a security group
get-group -id oldsamid | set-group -name newsamid -displayName "newdisplayName" -whatif

Connect with remote powershell to a Lync server
$lync = "lync01"; $session = New-PSSession -ConnectionUri "https://$lync/OcsPowershell" -Authentication Negotiate; Import-PsSession $session

Update the SIP address of a Lync user
Set-CsUser -Identity "user01" -SipAddress "sip:[email protected]" -whatif; get-csuser -id user01 | select SipAddress

Find Server 2012 firewall profiles
Get-NetFirewallProfile

Set Server 2012 firewall profiles to lock dropped traffic
Get-NetFirewallProfile  | Set-NetFirewallProfile -logBlocked "True"

Find the last known SCM message for the specified service starting
get-winevent  -computername fim01 -FilterHashTable @{ logname = "System"; providername="Service Control Manager"; ID = 7036; data = "Forefront Identity Manager Synchronization Service","Running"} -MaxEvents 1

Find the process creation date of a remote process
(Get-WmiObject -ComputerName fim01 -Query "Select * from win32_process where name ='miiserver.exe'") | select Name,@{N='Date';E={$_.ConvertToDateTime($_.creationdate)}} | ft -wrap -auto

Find if an AD account is locked out or not
get-aduser -id user01 -server dc01 -prop LockedOut

Start and then stop a network capture trace on server 2012
netsh Trace start capture = yes & pause & Netsh Trace stop

List the classes in a WMI namespace
Get-WmiObject -list -Namespace root\rsop\computer

Query the highest precedence logon as a service right GPO
Get-WmiObject -computer server01 -namespace root\rsop\computer -class RSOP_UserPrivilegeRight | where {$_.UserRight -eq 'SeServiceLogonRight' -and $_.Precedence -eq 1} | select-object -expand AccountList

Show the last 15 errors in the application event log
get-winevent  -computername server01 -FilterHashTable @{logname = "Application"; level=2} -MaxEvents 15

Query Server 2012 for scheduled task information
Get-WMIObject -computer server01 -Namespace "root\Microsoft\Windows\TaskScheduler" -Query "SELECT * from MSFT_ScheduledTask"

Query the security descriptor of shares on a server
$shares = Get-WMIObject -Computer "server01" -Namespace root\cimv2 -Query "SELECT * from  Win32_LogicalShareSecuritySetting"

Generate a new GUID
[System.Guid]::NewGuid().ToString()

Generate a new GUID and return with braces
[System.Guid]::NewGuid().ToString("B")

Get an empty GUID (all zeroes)
[System.Guid]::Empty

List browser URLs and document titles for IE browser (not edge)
$urls = (New-Object -ComObject Shell.Application).Windows() | Where-Object {$_.LocationUrl -match "(^https?://.+)|(^ftp://)"}; $urls | select locationName,locationUrl | ft -wrap -auto

View ADFS tracing from the debug event log
get-winevent  -computername adfs01 -FilterHashTable @{ logname = "AD FS Tracing/Debug"} -oldest

View ADFS auditing for claim information
get-winevent  -computername adfs01 -FilterHashTable @{ logname = "Security"; providername="AD FS Auditing"; ID = 500,501} -MaxEvents 10 | select id,machineName,TimeCreated,Message | ft -wrap -auto

View the AD site name associated with the specified computer
dfsutil /sitename:server01

Find the .Net framework version the current PowerShell instance is using
[Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()

Store an encrypted password reversable only by the encrypting user
ConvertTo-SecureString -string "password" -asplaintext -force | ConvertFrom-SecureString | out-file -file c:\temp\password.txt

Encode a string to base64
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("Testing"))

Decode a base64 string to text string
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("VGVzdGluZw=="))

Install the ActiveDirectory module for PowerShell
Install-WindowsFeature -Name RSAT-AD-PowerShell

Export a certificate to binary format
Export-Certificate -FilePath c:\windows\temp\cert.crt -cert cert:localmachine\ca\9A26AAB090E0CD1F39B96731A4B49AAC65E7BEEA -type cert

Convert an octet stored byte array (eg. GUID) to hex string
[System.String]::Join('',( (get-adobject -id "uid=user01,OU=Users,DC=domain,DC=local" -server dc01 -prop objectguid).objectguid | ForEach-Object { $_.ToString('x2') }))

Check if a string is null or empty
if ([string]::IsNullOrEmpty($string) -eq $true){"True"}

List the UPN suffixes from a remote forest
(get-adforest -identity domain.local).upnsuffixes

List the PowerShell remoting endpoints
Get-PSSessionConfiguration

Read a certificate from file
Get-PfxCertificate -FilePath c:\temp\test.cer | fl *

Prevent PowerShell progress bars from displaying
$ProgressPreference = "SilentlyContinue"

Convert the number of seconds to a timespan to show hours/minutes etc
[timespan]::fromseconds(15*60*60)

Find a remote PowerShell session using WinRM
Get-WSManInstance -ConnectionURI http://server01:5985/wsman shell -Enumerate

Remove a remote PowerShell session using WinRM
Remove-WSManInstance -ConnectionURI http://localhost:5985/wsman shell @{ShellID="6CF3C5C6-1954-430F-98B7-2D99E8AADCE3"}

Start an elevated process with PowerShell
start-process -verb RunAs cmd

Find the verbs available for a particular file
$startExe = New-Object System.Diagnostics.ProcessStartInfo -Args PowerShell.exe; $startExe.verbs

Check if a specified time of day has passed
((get-date) -lt ([datetime]::ParseExact("23:00:00", "HH:mm:ss", [System.Globalization.CultureInfo]"en-AU")))

Start an elevated runas process as alternate credentials
Start-Process powershell -Credential $cred -ArgumentList '-noprofile -command &{Start-Process cmd -verb runas}'

Find service terminated unexpectedly (multiple event IDs)
get-winevent  -computername server01 -FilterHashTable @{ logname = "System"; startTime = $date; id=7031,7034}

Decrypt a securestring password to text
(New-object System.Net.NetworkCredential("",$Password)).Password

Convert to a nicely formatted JSON message
ConvertFrom-Json $message | ConvertTo-Json

Find hotfixes installed
get-hotfix

URL encode a string
[System.Web.HttpUtility]::UrlEncode($clientID)

Check remote Hyper-V VM migration status
Get-WmiObject -computer server01 -Namespace root\virtualization\v2 -Class Msvm_MigrationJob | ft Name, JobStatus, PercentComplete, VirtualSystemName

Make it so doskey macros and shortcuts work in PS5+
Remove-Module PSReadLine

Find the digital signature of a file
(get-AuthenticodeSignature c:\util\procexp.exe).SignerCertificate | fl *

Convert Unix epoch time in milliseconds to datetime
(Get-Date "1970-01-01 00:00:00.000Z") + ([TimeSpan]::FromMilliSeconds(1539045767455))

Convert a number to binary
[convert]::ToString(512,2)

Convert from win32 filetime
"{0:hh:mm:ss.fff tt dd/MM/yyyy}" -f [datetime]::FromFileTime(131864751713547989)

Find the effective applocker policy
Get-AppLockerPolicy -Effective | Test-AppLockerPolicy -Path "C:\Windows\System32\cscript.EXE"

Determine whether the AD recycle bin is enabled or not (EnabledScopes)
Get-ADOptionalFeature -Filter 'name -like "Recycle Bin Feature"'

Find the Active Directory schema version
Get-ADObject (Get-ADRootDSE).schemaNamingContext -Property objectVersion

Query SCCM for a computer resource
$server = "sccm01";$site = "s01"; $resourceName = "server01"; $resource = Get-WmiObject -ComputerName $server -Namespace "root\sms\site_$site" -Class "SMS_R_System" -Filter "Name = '$resourceName'"

Query SCCM for a the collection membership of a computer resource
$ids = (Get-WmiObject -ComputerName $server -Namespace "root\sms\site_$site" -Class SMS_FullCollectionMembership -filter "ResourceID=`"$($Resource.ResourceId)`"").collectionID

Export DNS zone information from a 2016 DC
Get-DnsServerZone | export-csv -path c:\windows\temp\DNSZones_20190304.csv -encoding ascii -notype

Find Active Directory replication conflict objects
$conflicts = Get-ADObject -LDAPFilter "(|(cn=*\0ACNF:*)(ou=*CNF:*))"

Install RSAT on Windows 10 1809
Get-WindowsCapability -Name RSAT* -Online | Add-WindowsCapability -Online

Find Office 365 Group mailbox folder information
get-mailboxfolderstatistics -id [email protected] | select FolderPath

Find events containing the specified string
$filter =  @{logname='Security'; starttime=[datetime]::today; data='/adfs/services/trust/2005/windowstransport' }; $events = get-winevent -computername adfs01 -FilterHashTable $filter

Decode a dnsrecord entry in an AD DNSRecord object
$dnsrecord = (get-adobject -id "DC=10.10,DC=168.192.in-addr.arpa,CN=MicrosoftDNS,DC=ForestDnsZones,DC=domain,DC=local" -prop *).dnsrecord; [System.Text.Encoding]::ASCII.GetString($dnsrecord[0])

Report when a TCP connection was created
Get-NetTCPConnection | Sort-Object LocalPort | Format-Table Local*, Remote*, State, CreationTime

Find any alternate data streams in a file
get-item c:\temp\test.txt -Stream *

View the content of an alternate data stream
get-content c:\temp\test.txt -Stream Stream1

Remove an alternate data stream
Remove-Item -path c:\temp\test.txt -Stream Zone.Identifier

Find the direct reports from the AD manager backlink
(get-aduser -id user01 -prop directreports).directreports

Find the day of week
(get-date).DayOfWeek

Find the number of the current day of the week
[int](get-date).DayOfWeek | (get-date).DayOfWeek.value__

Find the number of the the specified day
[int][DayofWeek]"Sunday"

Format a string as hex
"05bb80f4-5d0b-4358-b173-7a206a924734" | format-hex

Export DNS SRV records
Get-DNSServerResourceRecord -ZoneName domain.local -ComputerName dc01 -RRType SRV | Export-CSV -path c:\temp\srv-export.csv -notypeinformation

Query Domain Controllers in one or more sites
Get-ADDomainController -filter "site -eq 'site1' -or site -eq 'site2' -or site -eq 'site3'" |select name

Show datetime on command prompt
function prompt { "PS $((Get-Date).ToString("hh:mm:ss")) $(get-location)>"}

Find DCs running 2016 OS
Get-ADDomainController -filter "OperatingSystem -eq 'Windows Server 2016 Standard'" | select name

Unblock a file downloaded from the Internet
Unblock-File C:\temp\downloaded.ps1

Find the 5 most recent files from the specific path
Get-ChildItem -Recurse -path c:\admin\scripts\powershell\*.ps1 | sort -prop LastWriteTime -desc | select -first 5 FullName,LastWriteTime

Find the registered event log sources for the specified log
Get-WMIObject -Computer "server01" -Authentication PacketPrivacy -Query "SELECT FileName, Sources from Win32_NTEventLogFile where FileName = 'CustomEventLog'" | select -expand sources


Wayne's World of IT (WWoIT). 


Read more!

Sunday, June 14, 2020

Exchange PowerShell Commands #2

Well it's 2020, and instead of flying around in a hovercar I'm *still* doing IT. Trying to get back in the swing of things – and following on from the last post seven year ago (!?), here's an additional 64 Exchange commands circa Exchange 2013/2016.

Each command can be copied and pasted into a PowerShell command line running with an Exchange PSSession, although at the time I made notes on a couple of them about running locally on the server due to cmdlet or type casting limitations.



Remove an existing Exchange 2010 move request (to allow subsequent moves)
Remove-MoveRequest -id user01

Initiate an Exchange 2010 intra-org move request
new-moverequest -id user01 -TargetDatabase db02

Output semi-colon delimited list of email addresses for a mailbox
[string]::Join(';',$mailbox.emailaddresses)

Convert a mailbox to a standard user mailbox
get-mailbox user01 | set-mailbox -type regular -whatif

Modify permissions to allow modifying apply policy attributes of user objects
Add-ADPermission -Identity "OU=Resources,DC=domain,DC=local" -InheritanceType Descendents -InheritedObjectType user -AccessRights ReadProperty, WriteProperty -Properties msExchPoliciesIncluded, msExchPoliciesExcluded -User "domain\group1" -domaincontroller dc01

Disable an Exchange 2007 mailbox - leaving the user account in AD
Disable-Mailbox -id user01

Reapply the mandantory properties on a mailbox (changing from linked to normal)
set-mailbox -id $account -applymandatoryproperties  #Clearing AEA and master account SID resets the isLinked property of the mailbox, but RecipientTypeDetails / RecipientDisplayType is not automatically updated.  Re-applying mandatory properties corrects this issue and sets the mailbox back to being a user mailbox instead of a linked mailbox

Find Exchange 2010 DAG status
Get-DatabaseAvailabilityGroup -status | fl *

Find Exchange 2010 DAG replication status
$servers = Get-MailboxServer; foreach ($server in $servers) { Get-MailboxDatabaseCopyStatus -server $server.name}

Find the mailbox activation preference for an Exchange 2010 mailbox database
(Get-MailboxDatabase -id MBXDB1).ActivationPreference

Report the last 1000 commands executed
Get-history -count 1000

Reseed the content index catalog for the database copy
Update-MailboxDatabaseCopy "DB\SERVER" -CatalogOnly

Get the mailbox database copy status
Get-MailboxServer | Get-MailboxDatabaseCopyStatus -ExtendedErrorInfo | sort-object ContentIndexState | fl Name,ContentIndexState,ContentIndexErrorMessage

Convert a user mailbox to a linked mailbox without disabling
Set-User -id user01 -LinkedMasterAccount domainb\user01 -LinkedDomainController dc01.domainb.com -LinkedCredential $domainbcred # may need get-mailbox -id user01 | set-mailbox -type regular; get-mailbox -id user01 | set-mailbox -ApplyMandatoryProperties

Find the current owner of Exchange 2010 mailbox databases
dsquery * "CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=Dept,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=domain,DC=local" -filter (objectclass=msExchMDB) -attr Name msExchOwningServer -s dc2

Mount an Exchange 2010  public folder database
Get-PublicFolderDatabase -id 'Public Folder Database 1' | mount-database

Find an Exchange 2010 Primary Active Manager for the specified DAG
Get-DatabaseAvailabilityGroup -Status | fl PrimaryActiveManager

Configure the Exchange 2010 postamaster address
Get-TransportConfig | fl -ExternalPostmasterAddress; Set-TransportConfig -ExternalPostmasterAddress [email protected]

Enumerate outlook profiles to find connected mailboxes and calendars
EnumOutlookProfiles.ps1

Find if an Exchange 2010 mailbox is enabled for ActiveSync
get-casmailbox -id user1

Set Exchange 2010 calendar processing to anyone can book and autoaccept
Set-CalendarProcessing -Identity room01 -AutomateProcessing AutoAccept -AllBookInPolicy $true

Get Exchange 2010 mailbox calendar configuration
Get-MailboxCalendarConfiguration -id room01 | fl *

Force an update of the offline address book
Update-OfflineAddressBook -id 'Default Offline Address List'

Exchange query for Global Security groups
get-group -filter "GroupType -eq $([Microsoft.Exchange.Data.Directory.Recipient.GroupTypeFlags]::SecurityEnabled.value__ + [Microsoft.Exchange.Data.Directory.Recipient.GroupTypeFlags]::Global.value__)"; #get-group -filter {GroupType -eq '-2147483646'}

Exchange query for Domain Local Security groups
get-group -filter "GroupType -eq $([Microsoft.Exchange.Data.Directory.Recipient.GroupTypeFlags]::SecurityEnabled.value__ + [Microsoft.Exchange.Data.Directory.Recipient.GroupTypeFlags]::DomainLocal.value__)"; #get-group -filter {GroupType -eq '-2147483644'}

Exchange query for Universal Security groups
get-group -filter "GroupType -eq $([Microsoft.Exchange.Data.Directory.Recipient.GroupTypeFlags]::SecurityEnabled.value__ + [Microsoft.Exchange.Data.Directory.Recipient.GroupTypeFlags]::Universal.value__)"; #get-group -filter {GroupType -eq '-2147483640'}

Set Out Of Office on a mailbox
Set-MailboxAutoReplyConfiguration 'user1' -AutoReplyState enabled -ExternalAudience all -InternalMessage "Internal not Here" -ExternalMessage "External not here"

Get a role group from the root of the forest
Get-RoleGroup -id Exchange-Lvl1Support -DomainController dc1.forest.root

Get a role group and the roles and role assignments
$rg = Get-RoleGroup -id Exchange-Lvl1Support -DomainController dc1.forest.root; $rg.roles; $rg.roleassignments

Get a management scope to show the recipient root and filter
Get-ManagementScope -id MS-Exchange_Distribution-Groups | fl *

Get the management role assignments for the specified assignee
Get-ManagementRoleAssignment | where {$_.RoleAssigneeName -like '*Exchange-Lvl1Support*'} | ft -wrap -auto

Get the management role assignment to confirm the CustomRecipientWriteScope
Get-ManagementRoleAssignment -id Distribution-GroupCreation | fl *

Expand the e-mail addresses of a mailbox to one per line
get-mailbox -id user1 | select -expand emailaddresses

Find Distribution List expansion
$expandDL = foreach ($server in Get-TransportServer) {Get-MessageTrackingLog -EventID EXPAND -resultsize unlimited -server $server | select "Timestamp","ServerHostname","EventId",{$_.RelatedRecipientAddress},{$_.Sender},"MessageSubject"}

Convert a mailbox from linked to resource
$ADsPath = "LDAP://CN=user1,OU=Shared Mailboxes,DC=domain,DC=local"; $user = [ADSI]$ADsPath ; $user.putex(1,"msExchMasterAccountSid",$null); $user.setinfo(); get-mailbox -id user1 | set-mailbox -ApplyMandatoryProperties; get-mailbox -id user1 | set-mailbox -type shared

Find mailboxes on a specific version of exchange (2010)
$mailboxes = get-mailbox -filter {ExchangeVersion -eq 44220983382016} -resultsize unlimited

Find the Exchange 2010 SP1+ Address Book RPC port
reg query \\cas1\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\MSExchangeAB\ParametersSystem /v RpcTcpPort

Find the Exchange 2010 RPC port
reg query \\cas1\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\MSExchangeRPC\ParametersSystem /v "TCP/IP Port"

Mail-enable an existing AD contact
Enable-MailContact -id "CN=User 01,OU=Contacts,DC=domain,DC=local" -ExternalEmailAddress [email protected]

Modify the MRS mailbox replication for move requests
notepad "\\cas1\c$\Program Files\Microsoft\Exchange Server\V14\Bin\MSExchangeMailboxReplication.exe.config" (MaxActiveMoves* and MaxTotalMovesPerMRS)

Find Exchange 2010 recoverable deleted items
Get-MailboxFolderStatistics -Identity "[email protected]" -FolderScope RecoverableItems | ft -wrap -auto

Export mailbox to PST in EX2010 (requires Exchange Trusted Subsystem ACLs)
New-MailboxExportRequest -Mailbox user@domaincom -FilePath \\cas1\d$\martinwa.pst

Check the move history of a mailbox
get-mailboxstatistics -id "CN=user1,DC=domain,DC=local" -includemovehist | fl Identity,MoveHistory

Check Exchange 2010 logon statistics to see who is logging on to a mailbox
get-mailbox -id user1 | get-logonstatistics | ft UserName,ServerName,LogonTime,LastAccessTime,Windows2000Account -wrap -auto

Set mailbox delivery restrictions to only allow accepting messages from (ndr)
Set-Mailbox -Identity user1 -AcceptMessagesOnlyFrom @{add="user2"} -whatif

Use Exchange 2010 cmdlets to change the CN/DisplayName of a security group
get-adgroup -id oldsamid | set-adgroup -SamAccountName newsamid -whatif

Find safe and blocked senders configured through Outlook for each mailbox
$mailboxes | Get-MailboxJunkEmailConfiguration | export-csv -path c:\temp\JunkEmailConfiguration_20140625.csv

Delete all the items from an Exchange 2010 mailbox
Search-Mailbox -id [email protected] -DeleteContent -confirm:$false -force

restore specific folders from a disconnect mailbox data to a recovery mailbox
New-MailboxRestoreRequest -BatchName Recovery -Name Test.User -SourceDatabase 'MBXDB5' -SourceStoreMailbox "2e94ffc9-9dad-42b1-92ec-5767d11051e4" -excludedumpster -includeFolders "Contacts/Misc", "Contacts/MISC/SHOPPING", "Contacts/MISC/Work Related" -TargetMailbox "recovery1" -AllowLegacyDNMismatch -whatif

Find Exchange 2010 RBAC management role entries for the specified role
Get-ManagementRole  -id RecipientCreationUserMailboxes | select -expand roleentries;  $entries = (Get-ManagementRole  -id RecipientCreationMailUser).RoleEntries; $entries | %{$_.split()[1]}

Find Exchange 2010 RBAC management scope OU and filter for the specified scope
Get-ManagementScope -id Managed-Users_UserMailboxes  | fl Identity,RecipientFilter,RecipientRoot

Search Exchange 2010 Admin Audit log for changes made by Exchange
Search-AdminAuditLog | ft -wrap -auto

export Exchange 2010 Admin Audit Log for new-mailbox requests and callers
$newUsers = Search-AdminAuditLog -cmdlets New-Mailbox,New-MailUser; $newUsers | select RunDate,ObjectModified,CmdletName,Caller,OriginatingServer | export-csv -path c:\temp\ExchangeUsers_20141020.csv -encoding ascii

Get Exchange mail queues on the specified server
get-queue -Server mail02

Get Exchange mail queue information
get-queue -Identity MAIL02\281950 | fl DeliveryType,NextHopDomain,Status,MessageCount,LastError

Find disconnected mailboxes (run on console of exchange server, not session)
get-mailboxdatabase | Get-MailboxStatistics | where {$_.displayName -eq 'User 01' -and $_.DisconnectDate -ne $null}

Report AD permissions through Exchange cmdlets
Get-ADPermission -id "OU=Distribution Lists,OU=Resources,DC=domain,DC=local" |ft Identity,User,InheritedObjectType,ExtendedRights,Properties,ChildObjectTypes,AccessRights -wrap -auto

Add an address to an exchange 2010 object
Set-MailUser -id "user1" -EmailAddresses (((Get-MailUser -id "user1").EmailAddresses)+="smtp:[email protected]") -whatif

Remove an address from an exchange 2010 object
$recipient = "user1"; $remove = "smtp:[email protected]"; Set-MailUser -id $recipient -EmailAddresses ((Get-MailUser -id $recipient).EmailAddresses | where {$_ -ne $remove}) -whatif

Remove an address from an exchange 2010 object with AD cmdlets
$recipient = "user1"; $remove = "smtp:[email protected]"; Set-ADUser -id $recipient -replace @{proxyAddresses=([system.object[]](Get-ADUser -id $recipient -prop proxyAddresses | Update-List -Property proxyAddresses -Remove $remove).proxyaddresses)} -whatif

Find Outlook Auto Mapping attributes for links/backlinks
get-adobject -LDAPFilter "(&(objectclass=user)(objectcategory=person)(|(msexchdelegatelistbl=*)(msexchdelegatelistlink=*)))" -prop displayname,msexchdelegatelistbl,msexchdelegatelistlink | select displayname,msexchdelegatelistbl,msexchdelegatelistlink | ft -wrap -auto

Exchange cmdlet to query the UPN suffix list
Get-UserPrincipalNamesSuffix

Find people that have upgrated to iOS 7.0 on EAS connected devices
get-adobject -ldapfilter "(&(objectClass=msExchActiveSyncDevice)(objectCategory=msExchActiveSyncDevice)(msExchDeviceOS=ios*7.0*))" -prop msExchDeviceOS,canonicalName -server dc1

Apply a throttling policy association to a standard user or MEU
Set-ThrottlingPolicyAssociation -Identity svc_mw -ThrottlingPolicy Office365MigrationWiz

Wayne's World of IT (WWoIT). 


Read more!

Saturday, August 14, 2010

Resizing a VM's Windows system OS with Set-HardDisk

With vSphere PowerCLI Update 1 there is now an automated method of increasing the system disk of a virtual machine. This essentially performs the same thing as I’ve been doing manually for a while now - the disk is resized and mounted to a ‘helper vm’ and diskpart extends the disk, the helper VM is then shutdown and the risk removed, and when the resized VM starts up the OS prompts for a final reboot after detecting the disk change.

Note that both the target and helper VM must be powered off, and credentials must be passed or the helpervm argument is ignored. The guest credentials are used on the Windows helper VM to authenticate using the VMtools mechanism to invoke a command inside a VM.


$guetsusername = "domain\username"
$guestpass = Read-Host "Guest password (administrator of the server)?" -assecurestring
$hostusername = "root"
$hostpass = Read-Host "Host Password (VI SDK access to the ESX host)?" -assecurestring
$hostcredential = new-object System.Management.Automation.PSCredential($hostusername, $hostpass)
$guestcredential = new-object System.Management.Automation.PSCredential($guetsusername, $guestpass)
$sizeGB = 30
$sizeKB = $sizeGB *1024*1024
$harddisk = Get-HardDisk -vm vm01
Set-HardDisk -HardDisk $harddisk[0] -capacityKB $sizeKB -hostcredential $hostcredential -guestcredential $guestcredential -helpervm $vm



An error occurred when trying to use domain credentials to pass to the vCenter SDK – host credentials are required, not credentials to vCenter (which seems odd).

Set-HardDisk -HardDisk $harddisk[0] -capacityKB 31457280 -hostcredential $hostcredential -guestcredential $guestcredential -helpervm helpervm
Set-HardDisk : 4/02/2010 1:25:04 PM    Set-HardDisk    919835D1-AD13-41DF-ABDE-D630E96FE722    While performing operation 'Connect to host service 'https://esx01/sdk' at port 902' the following error occured: 'Insufficient permissions in host operating system'
At line:1 char:13
+ Set-HardDisk  <<<< -HardDisk $harddisk[0] -capacityKB 31457280 -hostcredential $hostcredential -guestcredential $guestcredential -helpervm helpervm




Another error occurred when trying to resize a VM, the script used failed.  Note that I tried this manually and it also failed (must have been something particular to that VM).  It was interesting to see that from this it looks like the cmdlet uses the invoke-vmscript to run a remote script through the VMtools interface that calls the diskpart commands.
Set-HardDisk : 4/02/2010 11:22:11 AM    Set-HardDisk    919835D1-AD13-41DF-ABDE-D630E96FE722    Execution of the  script in guest OS on VM 'helpervm' failed: '
Microsoft DiskPart version 5.2.3790.3959
Copyright (C) 1999-2001 Microsoft Corporation.
On computer: HELPERVM
Disk 1 is now the selected disk.
Volume 2 is the selected volume.
The volume you have selected may not be extended.
Please select another volume and try again.
'
At line:1 char:13
+ Set-HardDisk  <<<< -HardDisk $harddisk[0] -capacityKB 31457280 -hostcredential $hostcredential -guestcredential $guestcredential -helpervm $vm



Wayne's World of IT (WWoIT), Copyright 2010 Wayne Martin. 


Read more!

Sunday, May 9, 2010

Keeping Citrix sessions alive with sendkeys

I use the following script as a way of keeping my Citrix session alive to thwart security guys and their over-aggressive timeouts.

Normally I don't like to use sendkeys, but this seemed like the perfect way to trick ica into thinking keys were being pressed...

PowerShell and VBScript versions below.



#
# Find the wfica.exe process and poke keystrokes at it, preventing a Citrix session from timing out
# Be careful what you have open and what keystrokes you poke

[System.Object[]]$processes = get-process | where {$_.ProcessName -eq "wfica32.exe"}
if ($processes.count -ge 1) { $process = $processes[0] }

if ($process -is [System.Diagnostics.Process])
{
  $processes | format-list -property *

  for (;;)
  {
    [void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")

    [Microsoft.VisualBasic.Interaction]::AppActivate($process.Id)
    start-sleep -seconds 1

    [void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
    [System.Windows.Forms.SendKeys]::SendWait("1")
    start-sleep -seconds 240
  }

}





Set objWshShell = CreateObject("Wscript.Shell")

strProcessName = "wfica32.exe"

intProcessID = 0
Call FindProcessID(strProcessName, intProcessID)

if (intProcessID > 0) Then
 wscript.echo "Found process ID: " & intProcessID

 Do 
  objWshShell.AppActivate(intProcessID)
  WScript.Sleep 1000
  objWshShell.SendKeys "1"
  WScript.Sleep 240000
 Loop

End If


Function FindProcessID(ByRef strProcessName, ByRef intProcessID)

 strQuery = "Select * from Win32_Process Where Name = '" & strProcessName & "'" ' WQL query string looking for the specified process

 Set objNameSpace = GetObject("winmgmts://./root/cimv2")    ' The cimv2 namespace of the local machine

 FindProcessID = vbFalse
 Set objProcessSet = objNameSpace.ExecQuery(strQuery)     ' Execute the query
 For Each objProcess in objProcessSet      ' For each process in the set, ordered by oldest to newest
  intProcessID = objProcess.ProcessID
  WScript.Echo "Found process, ID: " & intProcessID
  WScript.Echo objProcess.Name & ", " & intProcessID & ", " & objProcess.CreationDate & ", " & objProcess.CommandLine & ", " & objProcess.Priority & ", " & objProcess.WorkingSetSize & ", " & objProcess.PageFileUsage
  FindProcessID = vbTrue
 Next

 Set objNameSpace = Nothing : Set objProcessSet = Nothing : Set objProcess = Nothing
End Function


Wayne's World of IT (WWoIT), Copyright 2010 Wayne Martin. 


Read more!

Thursday, January 14, 2010

PowerCLI: Copy-VMGuestFile

In VMware PowerCLI Update 1, a new cmdlet has been added to copy files to/from guest OS’s using the VMTools. Under normal circumstances this wouldn’t be all that useful, but if a machine is not accessible on the network – either a DMZ or a test-lab environment for example – there is now a method to easily copy files to and from the guest machines.

For example:

copy-vmguestfile -source c:\windows\system32\imageres.dll -destination c:\temp\ -vm vm01 -guesttolocal -hostuser root -hostpassword * -guestuser administrator -guestpassword *

Note that you need to specify the passwords, or not specify credentials at all for a prompt, or you can use a pscredential object.

Unfortunately wildcards are currently unsupported, but it would be easy to loop through local files within PowerShell to upload based on wildcards.

I ran this with the measure-command cmdlet to see how fast it was – about 0.5MB/sec– not too quick, but a lot better than nothing.

measure-command -expression {copy-vmguestfile -source c:\windows\system32\imageres.dll -destination c:\temp\ -vm vm01 -guesttolocal -hostuser root -hostpassword * -guestuser administrator -guestpassword *}

Seconds : 26
Milliseconds : 539
Ticks : 265390633
TotalDays : 0.000307165084490741
TotalHours : 0.00737196202777778
TotalMinutes : 0.442317721666667
TotalSeconds : 26.5390633
TotalMilliseconds : 26539.0633


Wayne's World of IT (WWoIT), Copyright 2010 Wayne Martin. 


Read more!

Friday, October 30, 2009

VMware PowerCLI commands

The VMware PowerCLI PowerShell interface provided for managing vSphere systems is a fantastic tool that should be useful for all VMware admins.

I've gathered these commands while implementing and managing ESXi 4.0 clusters, use with caution on any production system.

This is an extension of a previous post:
VMware vSphere PowerCLI commands



Join a cluster by moving an ESX host from one location to the cluster
Move-Inventory -Item (Get-VMHost -Name esxHost) -Destination (Get-Cluster -Name clusterName)

Get the VMware.Vim.ClusterComputeResource MO from the PowerCLI cluster object
$clusterview = get-view $cluster.Id

Reconfigure a host for VMware HA (high availability)
$vmhost = get-vmhost -name esxHost; $hostMO = Get-View -ID $vmhost.ID; $hostMO.ReconfigureHostForDAS()

Find migration events for the last day
$events = Get-VIEvent -Start (Get-Date).AddDays(-1) | where {$_.fullFormattedMessage -match "Migrating.*"}

Find events other than CPU Alarms or user login/logout for the last day
$events = Get-VIEvent -Start (Get-Date).AddDays(-1) | where {$_.fullFormattedMessage -notmatch "Alarm.*CPU.*|User.*logged.*"}

Find events for degraded MPIO path redundancy 
$events = Get-VIEvent -Start (Get-Date).AddDays(-1) | where {$_.fullFormattedMessage -match "Path redundancy to storage.*degraded"}

Report the date, host and description for MPIO path redundancy errors
foreach ($event in $events) {write-output ($event.createdTime.ToString() + "," + $event.host.get_name() + "," + $event.fullFormattedMessage)}

List a table of VI events with only the date and message
$events | format-table -wrap -autosize -property createdTime,fullFormattedMessage

List the physical networks adapters and the current link speed (ESX 4.0)
$hostSystem = get-view -ViewType HostSystem; $hostConfigManager = $hostSystem.get_ConfigManager(); $hostNetworkSystem = $hostConfigManager.get_NetworkSystem(); $netSystem = Get-View $hostNetworkSystem; $netSystem.NetworkConfig.pnic; foreach ($pnic in  $netSystem.NetworkConfig.pnic) {Write-Output ($pnic.Device + "," + $pnic.spec.linkspeed.SpeedMB)}

List the vSwitches and the uplinks currently attached
$hostSystem = get-view -ViewType HostSystem; $hostConfigManager = $hostSystem.get_ConfigManager(); $hostNetworkSystem = $hostConfigManager.get_NetworkSystem(); $netSystem = Get-View $hostNetworkSystem; foreach ($vswitch in  $netSystem.NetworkConfig.vSwitch) {Write-Output ($vSwitch.Name + "," + $vswitch.spec.policy.NicTeaming.NicOrder.ActiveNic)}

Remove snapshots from a group of machines
$VMs = Get-VM -Location (get-folder -name "vmFolder"); foreach ($vm in $vms) {remove-snapshot -snapshot (Get-Snapshot -vm $vm) -confirm:$false}

Take snapshots of a group of machines
$VMs = Get-VM -Location (get-folder -name "vmFolder"); foreach ($vm in $VMs) {New-Snapshot -Name "snapshot 01" -description "Snapshot description" -vm $vm -Quiesce:$false}

Find VM name, description and primary disk datastore
$VMs = get-vm; foreach ($vm in $VMs) {write-output ($vm.Name + ",""" + $vm.Description + """," + $vm.harddisks[0].FileName.Replace(" ", ",")) | out-file -append -filepath c:\temp\VM_Datastores.txt}

Bring a host out of maintenance most
Set-VMHost -VMHost esxHost -State Connected

Generate diagnostic support bundles for all hosts
get-log -vmhost (get-vmhost) -bundle -destinationpath c:\temp\bundles

Find the network adapter type for each VM
$vms = get-vm ; foreach ($vm in $vms) {write-host $vm.Name "-"  $vm.networkadapters[0].type}

Find physical NICs and whether they're set to autonegotiate or hardcoded
foreach ($pnic in $hostNetwork.pnic) {if($pnic.linkSpeed -eq $null) {$ls = "Auto"} else {$ls= $pnic.linkSpeed.speedMB.toString() + ":" + $pnic.linkSpeed.duplex} ;write-output ($pnic.Device + "," + $ls)}

Find host sytem build information
$hostSystems = get-view -ViewType HostSystem; foreach ($hostSystem in $hostSystems) {Write-Output ($hostSystem.Name + "," + $hostSystem.config.product.Fullname)}

Find VMs and whether the VMtools is configured to synchronising time 
$vmSet = Get-VM ; foreach ($vm in $vmSet) { $view = get-view $vm.ID ;$config = $view.config; $tools = $config.tools; Write-Output ($vm.Name + "," + $tools.SyncTimeWithHost) }

Revert to a snapshot
set-vm -vm vmName -snapshot (get-snapshot -vm vmName) -confirm:$false

Remove a virtual machine from inventory and delete from disk
remove-vm -DeleteFromDisk:$true -RunAsync:$true -vm vmName

Shutdown one or more Virtual Machine guests
shutdown-vmguest -vm $vms -confirm:$false

Start one or more Virtual Machine guests
start-vm -vm $vms -confirm:$false

Forcefully power off one or more Virtual Machines
stop-vm $vms -confirm:$false

Get a virtual switch from the specified ESX host
get-virtualswitch -name vSwitch1 -vmhost esxHost

Create a new port group on the specified vSwitch
New-VirtualPortGroup -Name "pgName" -VirtualSwitch $vs

Find ESX memory balloon averages for the last five days
get-stat -entity $hosts -start (Get-Date).AddDays(-5) -finish (Get-Date) -stat mem.vmmemctl.average

Export a list of VMs
$vms | select-object -prop Name | out-file -filepath c:\temp\vms.txt

Export a list of VM guest hostnames 
$vms = get-vm; foreach ($vm in $vms) { write-output $vm.guest.get_HostName()}


Wayne's World of IT (WWoIT), Copyright 2009 Wayne Martin. 


Read more!

Tuesday, August 11, 2009

VMware PowerCLI - Reconfigure cluster options

The following script contains a simple example of how to modify the configuration of a cluster – in this case to add two advanced options to the HA settings. This uses the new ClusterConfigSpecEx object, which can be used with the more generic function to reconfigure a resource – ReconfigureComputeResource().

The options set in this example are the das.allowNetworkX options, configuring HA to set specific management networks/service consoles that are used for HA communication and heartbeats.

Consider the following scenario for a reason why you might want to use these options:

  1. Software iSCSI with ESX 3.x – using the service console for authentication, meaning you need a service console on your iSCSI network (or routes between your corporate network and iSCSI). The standard result is that HA could be used across this interface.
  2. You are progressing with an upgrade to ESXi 4.0, still using software iSCSI, which is improved to remove the need for a management interface on your iSCSI network
  3. You put an ESX 3.5 and ESXi 4.0 host in the same cluster, HA no longer works because your iSCSI service console on ESX 3.5 can’t talk to your ESXi 4.0 host.
  4. Adding the two options above (with names changed appropriately) would force HA to use the service console on ESX 3.5 and the equivalent management network on ESXi 4.0, ignoring the iSCSI service console on 3.5 - resolving HA issues.



# http://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.ComputeResource.html#reconfigureEx
# http://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.cluster.DasConfigInfo.html#field_detail

param (
    $vcServerName = ""
)

$viServer = Connect-VIServer -server $vcServerName

$optionValue = New-Object Vmware.Vim.OptionValue
$optionValue.Key = "das.allowNetwork0"
$optionValue.Value = "Service Console"

$optionValue2 = New-Object Vmware.Vim.OptionValue
$optionValue2.Key = "das.allowNetwork1"
$optionValue2.Value = "Management Network"

[Vmware.Vim.OptionValue[]]$optionValues = $optionvalue, $optionValue2  # Create the array of the two new option values

$cluster = get-cluster -Name 'Cluster01'     # Get the cluster
$clusterview = get-view $cluster.Id      # Get the SDK object from the PowerCli object MO

$spec = New-Object Vmware.Vim.ClusterConfigSpecEx
$spec.dasConfig = New-Object Vmware.Vim.ClusterDasConfigInfo   # New VMware HA config

$spec.dasConfig.option = $optionValues      # Add the array of optionValues
$clusterview.ReconfigureComputeResource($spec, $true)    # Modify the configuration. When configuring clusters, can be a ClusterConfigSpecEx object


# Check the settings were saved

$cluster = get-cluster -Name 'Cluster01'     # Get the cluster
$clusterview = get-view $cluster.Id      # Get the SDK object from the PowerCli object MO

$clusterview.Configuration.dasConfig.Option | format-list   # Retrieve the advanced options


Wayne's World of IT (WWoIT), Copyright 2009 Wayne Martin. 


Read more!

VMware PowerCLI - Backup ESXi 4.0 firmware

The following script contains a simple function to use the VMware vSphere PowerCLI to connect to a vCenter and/or ESXi 4.0 host and backup the 'firmware' - the configuration of the ESX host. I don’t think this functionality is exposed directly through the PowerCLI, so a call to the very useful Get-View function is used to get the VI SDK object to call the appropriate method.

From what I can tell all this command does is generate a config dump on the server and return the HTTP URL to access the file download, which I’m then using the .Net web client object to download the file and store as the filename returned with a unique date suffix.

This certainly isn’t original, all I did was look at the vicfg-backup.pl perl RCLI script and (badly) translate to PowerCLI from there.


#
# Description:
#  Backup the firmware configuration on an ESXi 4.0 host
#
# Limitations:
#  -
#
# Assumptions, this script works on the assumption that:
#  The caller provides credentials with permissions to connect to the specified host
#
# Arguments:
#  esxServer, the ESX host to connect to, eg. esx01
#  vcServerName, The vCenter Server to connect to, eg vc01
#  outputDir, The directory to write the backup file to, defaults to %temp%
#  username, The username to use when connecting to the vCenter server (if specified), defaults to %username%
#  password, The password to use for the connection to vCenter, secure string prompt by default
#
# Usage:
#  PowerShell . .\BackupFirmware.ps1 -esxServer 'esx01'
#  PowerShell . .\BackupFirmware.ps1 -esxServer 'esx01' -vcServer vc01 -u domain\username
#
# References:
#  http://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.host.FirmwareSystem.html
#
# Changes:
#  04/07/2009, Wayne Martin, initial version

#$ErrorActionPreference = "Continue"


param (
    $esxServer = "",
    $vcServerName = "",

    $outputDir = $env:Temp,

    $username = $env:username,
    $password = ""
)

if ($password -eq "" -and !($pass)) {
    write-output "No password specified from the command-line"
    $pass = Read-Host "Password?" -assecurestring
    $credential = new-object System.Management.Automation.PSCredential($username,$pass)    
}

if ($esxServer -eq "" -OR ($vcServerName -eq "" -and $esxServer -eq "")) {
    Write-Output "Please specify either a standalone host, or a host and a cluster"
    Exit 2
}

$viServer = $null


function BackupConfiguration([string] $vcServerName, [string] $esxServer, [string] $outputDir){
    $hostSystem = get-view -ViewType HostSystem -Filter @{"Name" = $esxServer}    # Find the .Net view of the specified host

    $hostConfigManager = $hostSystem.get_ConfigManager()      # Get the config manager
    $hostfirmwareSystem = $hostConfigManager.get_firmwareSystem()     # Find the MOR of the host firmware system

    $hostfirmware = Get-View $hostfirmwareSystem        # Get the VMware.Vim.HostFirmwareSystem object from the MOR

    $backupDownload = $hostfirmware.BackupFirmwareConfiguration()     # Call the backup method to generate the config bundle

    $backupDownload = $backupDownload.Replace("*", $esxServer)      # Replace '*' with the server name

    Write-Output "Backup saved to $backupDownload on the ESX host"
    $fileName = $backupDownload.SubString($backupDownload.LastIndexOf("/")+1)    # Extract the filename to reuse
    $fileType = $fileName.SubString($fileName.LastIndexOf("."))      # Find the extension (.tgz in this case)

    $Now = [DateTime]::Now.ToString("yyyyMMddTHHmmss")          # Unique identifier for the filename
    $file = $fileName.SubString(0, $fileName.Length - $fileType.Length)     # File name without extension
    $outputFile = $outputDir + "\" + $file + "_" + $Now + $fileType     # Construct the full filename path\bundle_date.tgz

    $wc = new-object system.net.WebClient        # use the .Net web client
    $wc.DownloadFile($backupDownload, $outputFile)       # Download the file from the URL returned

    if (test-path -path $outputFile) {         # Does the output file exist?
        Write-Output "$outputFile downloaded"
    } else {
        Write-Output "Error: $outputFile was not downloaded from $backupDownload"
    }

}

if ($vcServerName -ne "") {
    $viServer = Connect-VIServer -server $vcServerName -Credential $credential
} elseif ($esxServer -ne "") {
    $esxServer = Connect-VIServer -server $esxServer       # connect to VC
}


$results = ""
$results = BackupConfiguration $vcServerName $esxServer $outputDir

$results

if ($vcServerName -ne "") {
    Disconnect-VIServer -confirm:$false
}

Wayne's World of IT (WWoIT), Copyright 2009 Wayne Martin. 


Read more!

Thursday, August 6, 2009

VMware vSphere PowerCLI commands

The commands below are PowerCLI commands used to automate VMware ESX and VirtualCenter at the command prompt. Most of these commands were built on 3.5 with vSphere PowerCLI, and the majority have been tested against ESXi 4.0 and vCenter 4.0 infrastructure.

Each command-line can be copied and pasted at a PowerCLI command prompt, and most commands assume you already have a connection to the target, be it vCenter or ESX.

Note that most of these commands use OBN - Object By Name - instead of using the get* command to get the object. This is supported with all but a few of the commands I've come across.



List the vSphere PowerCLI commands
Get-VICommand

Connect to a ESX or VirtualCenter instance
connect-viserver -server %server%

List the currently available datastores
Get-Datastore | sort

List the currently available datastores filtered and sorted
Get-Datastore | where {$_.Name -like '*pr*'} | sort

Find the VMs attached to one or more datastores
foreach ($prodDatastore in $prodDatastores) { write-output $prodDatastore.Name; get-vm -datastore $proddatastore; write-output ''}

Get a Virtual Machine
$vm = get-vm -name '%vm%'

Get the virtual harddisk for the specified VMs
Get-HardDisk -vm $vm

Move a virtual machine to another container
Move-VM -Destination $prodApps -VM $vm

Update the VM description for a list of CSV entries
foreach ($virtualServer in $virtualservers) {$arr = $virtualServer.split(","); $desc = $arr[1]; $vmName = $arr[0]; write-output $vmName; $desc; $vm = get-vm -name $vmName; Set-VM -VM $vm -description $desc}

Query for a list of VMs and output in ANSI format
get-vm | sort-object | format-table -property Name | out-file -encoding ASCII -filepath c:\temp\vms_20090625.txt

Find VMware machine performance statistics
 get-stat -entity $vm -disk -start 01/01/2009 -finish ([DateTime]::Now.ToString("dd/MM/yyyy"))

For a group of VMs, report performance statistics and save to file
foreach ($vm in $devVMs) {get-stat -entity $vm -disk -start 01/01/2009 -finish ([DateTime]::Now.ToString("dd/MM/yyyy")) | out-file -filepath ("c:\temp\" + $vm.Name + "DiskPerformance.txt")}

Find VM datastore disk usage
$devVMs = get-vm -name '*dv*'; foreach ($vm in $devvms) {$vm.harddisks}

Find VM datastore disk usage
$testVMs = Get-VM -Location (get-folder -name "Test") ;foreach ($vm in $testVMs) {$vm.harddisks | format-table -hideTableHeaders -wrap -autosize | findstr /i /c:per}

Find SCSI devices attached to an ESX server
get-scsilun -vmhost (Get-VMHost -Location "cluster")[0]

Rescan HBAs on an ESX server
get-VMHostStorage -VMHost (Get-VMHost -Location "cluster")[0] -RescanAllHba

Storage vMotion a virtual machine to a new datastore
Move-VM -vm "vmName" -datastore "NewDatastore"

Storage vMotion a group of machines from a CSV input file
$servers = get-content -path inputfile.txt; foreach ($server in $servers) {move-vm -vm $server.split(",")[0] -datastore $server.split(",")[1]}

Remove a snapshot and child snapshots, reporting how long the operation took
measure-command -expression {remove-snapshot -snapshot $snapshots[0] -removechildren}

Find datastore space, usage and number of VMs per datastore
$datastores = get-datastore | sort-object; write-output "Name,Size,Used,Free,% Used,#VMs"; foreach ($datastore in $datastores) { write-output ($datastore.Name + "," + [math]::round($datastore.CapacityMB/1024) + "," + [math]::round(($datastore.CapacityMB/1024)-($datastore.FreeSpaceMB/1024)) + "," + [math]::round($datastore.FreeSpaceMB/1024) + "," + [math]::round(((($datastore.CapacityMB/1024) - ($datastore.FreeSpaceMB/1024)) / ($datastore.CapacityMB/1024)) * 100) + "," + (get-vm -datastore $datastore).count)}

From a set of VMs, find which have snapshots
foreach ($testvm in $testvms) {if (get-snapshot -vm $testvm){write-output $testvm.Name}}

Find the size of the first hard disk in each VM
foreach ($vm in $vms) {$vm.harddisks[0] | format-table -hideTableHeaders -wrap -autosize | findstr /i /c:per }

Find disk information for VMs in the specified datastore
 $VMs = Get-VM ;foreach ($vm in $VMs) {$vm.harddisks | where {$_.FileName -like '*clusterpr*'} | format-table -hideTableHeaders -wrap -autosize | findstr /i /c:per}

Find VMs in the specified datastore
$VMs = Get-VM | where {$_.harddisks[0].FileName -like '*clusterpr*'}

Get VM guest information, including virtual OS
get-vm | get-vmguest | format-table -wrap -autosize

Find virtual machines and their description/notes
$vms = get-vm ; $vms | format-table -wrap -autosize -property Name,Description

Create an associative array containing VM names and descriptions
$vmdesc = @{}; foreach ($vm in $vms) {$vmdesc.add($vm.Name, $vm.Description)}

Migrate a virtual machine to another host in a VMware ESX cluster
move-vm -vm %vmName% -destination %hostname%

Find the host a VM is currently located on
get-vmhost -vm %vnName%

Add a new harddisk to a virtual machine
New-HardDisk -vm %vmName% -CapacityKB 20971520

Retrieve details on the resource pools from the currently connected datacenter
Get-ResourcePool | format-table -wrap -autosize -property Name,Id,CpuExpandableReservation,CpuLimitMHz,CpuReservationMHz,CpuSharesLevel,CustomFields,MemExpandableReservation,MemLimitMB,MemReservationMB,MemSharesLevel,Name,NumCpuShares,NumMemShares

Find virtual machines and if they have a CD-ROM
get-vm | format-table -wrap -autosize -property Name,CDDrives

Find the last 100 events that aren't alarm related
$events = Get-VIEvent -MaxSamples 100 | where {$_.fullFormattedMessage -notmatch "Alarm*"}

Find all events for machine deployments from templates
$events = Get-VIEvent | where {$_.fullFormattedMessage -match "Deploying (.*) on host (.*) in (.*) from template (.*)"}

Create a resource pool with high CPU and memory shares
New-ResourcePool -location (get-cluster -name 'cluster') -Name ResPool1 -CpuSharesLevel [VMware.VimAutomation.Types.SharesLevel]::High -MemSharesLevel [VMware.VimAutomation.Types.SharesLevel]::High

Create a folder from the root of the tree
New-Folder -Name Workstations -location (get-folder -name 'vm')

Move one or more VMs to a resource pool (or other destination)
$vms = get-vm -name vmNames*; move-vm -vm $vms -destination (Get-ResourcePool -name 'ResPool1')

Get an OS customization specification, and list the properties in wide format
Get-OSCustomizationSpec -name "SpecName" | format-list

Take a snapshot of a virtual machine
New-Snapshot -Name "Snapshot 01" -description "Snapshot description" -vm vmName -Quiesce:$true

Convert a virtual machine to a template
$vmView = get-vm -name vm01 | Get-View; $vmView.MarkAsTemplate()

Find Datastore usage (custom written function)
get-datastoreusage

Get an ESX log bundle using PowerCLI
Get-Log -VMHost esxhostname -Bundle -DestinationPath c:\temp

Query for snapshots
Get-VM | Get-Snapshot | export-csv -path c:\temp\VMsnapshots.csv

Query for snapshot information
Get-VM | Get-Snapshot | foreach-object {$out=  $_.VM.Name + "," + $_.Name + "," + $_.Description + "," + $_.PowerState; $out}


Wayne's World of IT (WWoIT), Copyright 2009 Wayne Martin. 


Read more!

Monday, July 27, 2009

PowerCli Relocate VM storage with VI35 / VI4

This post provides a script to help with storage vMotion – migrating a VMware virtual machine from one VMFS datastore to another with no downtime. It’s a great tool, but in VI35 at least it’s not exposed through the GUI or VC scheduled tasks, so I’ve written a script to perform a few checks and allow for very simplistic 'scheduling' (delay).

I’ve only tested this with VI3.5 and PowerCLI 1.0, but I have no reason to think this wouldn’t with with vSphere vCenter and VI 4.0 hosts.

There are a few caveats with storage vMotion (in VC 2.5/ESX 3.5) at least, you can’t:
- Move the storage of a VM that has a snapshot if the VM is powered on
- Move the storage of a VM that has a snapshot (in any power state) if the VM has disks in a different location than the config file.

Based on these caveats, if instructed the script will suspend a VM with snapshots in order to move the storage, then power the VM back on. Use this with caution, as this may cause an outage of your VMs.


#
# Description:
#   Relocate a virtual machine from one datastore to another.
#
# Limitations:
#  -
#
# Assumptions, this script works on the assumption that:
#   The caller provides credentials with permissions to perform the operations
#
# Arguments:
#  vmName, lowecase short name for the virtual machine, eg pibutepr03
#  dataStoreName, the new datastore to migrate the VM to
#  suspend, Whether or not to suspend a VM and move if the VM has snapshots (which prevent live storage vMotion vi VI35)
#  username, The username to connect with, default to the current username environment variable
#  password, The Password to use for the connection, not specifying a password will result in a prompt to enter a secure string
#  delay, The optional number of seconds to delay before starting the operation
#
#
# Usage:
#   PowerShell . .\RelocateVM.ps1 -vmName "vm01" -datastore 'ds02'
#   PowerShell . .\RelocateVM.ps1 -vmName "vm01" -datastore 'ds02' -suspend yes -username domain\user
#
# Changes:
#  07/04/2009, Wayne Martin, Initial version
#

param (

    $vmName = "",
    $dataStoreName = "",
    $suspendIfRequired = $false,
    $username = $env:username,
    $password = "",
    $delay = 0
)


$ErrorActionPreference = "Continue"

 $invalidArgs = $false
if ($dataStoreName -eq "") { write-output "Please specify the datastore target for the VM, eg. ds02"; $invalidArgs = $true}
if ($vmName -eq "") { write-output "Please specify the virtual machine to move, eg vm01"; $invalidArgs = $true}
if ($account -eq "") { write-output "Please specify a user account to connect with, eg domain\user"; $invalidArgs = $true}

if ($invalidArgs) { write-output "Invalid Arguments, terminating"; exit}


Write-Output "Moving VM '$vmName' to the '$dataStoreName' datastore"

if ($delay -gt 0) {
    $hours = $delay / 60 /60
    Write-Output "Delaying $delay seconds before beginning ($hours hours)"
    Sleep -seconds $delay
}


if ($suspendIfRequired) {
    Write-Output "The virtual machine will be suspended if snapshots are preventing storage vMotion"
} else {
    Write-Output "If the virtual machine has snapshots and is powered on the storage vMotion will not work"
}

if ($password -eq "" -and !($pass)) {
    write-output "No password specified from the command-line"
    $pass = Read-Host "Password?" -assecurestring
}
$credential = new-object System.Management.Automation.PSCredential($username,$pass)    


$viServer = $null
$suspend = $false
$poweredOn = ""
$snapshot = $null
$hasSnapshot = $null

$viServer = Connect-VIServer -server $vcServerName -Credential $credential


if ($viServer) {
    Write-Output (Get-Date -format "dd/MM/yyyy HH:mm:ss")
    write-output ("Connected to server " + $viServer.Name + " on port " + $viServer.Port)

    $vm = get-vm -name $vmName
    if ($vm -and $vm -isnot [object[]]) {
        Write-Output ("Found " + $vm.Name)

        $hardDisks = $vm.hardDisks
        $vmSize = 0
        $vmSizeMB = 0
        foreach ($harddisk in $vm.hardDisks) {
            $vmSize += $hardDisk.CapacityKB
        }
        $vmSizeMB = $vmSize /1024

        $datastore = get-datastore -name $dataStoreName
        if ($datastore) {
            $freeSpace = $datastore.FreeSpaceMB
        
            if ($freeSpace -gt $vmSizeMB)
            {
                Write-Output "The datastore $datastoreName has $freeSpace MB available, the VM has disks totalling $vmSizeMB MB"
            
                switch ($vm.PowerState)
                {
                    ([VMware.VimAutomation.Types.PowerState]::PoweredOn)
                    {
                        Write-Output "The virtual machine is currently powered on"
                        $poweredOn = $true
                    }

                    ([VMware.VimAutomation.Types.PowerState]::PoweredOff)
                    {
                        Write-Output "The virtual machine is currently powered off"
                    }

                    ([VMware.VimAutomation.Types.PowerState]::Suspended)
                    {
                        Write-Output "The virtual machine is currently suspended"
                    }
            
                    default
                    {
                        write-output "Virtual machine power state unknown"
                    }
                }

                [object[]]$snapshot = get-snapshot -vm $vm
                if ($snapshot) 
                {
                    $hasSnapshot = $true
                    $numSnapshots = $snapshot.Count
                    Write-Output "$vmName currently has $numSnapshots snapshot(s)"
                    if ($poweredOn) {
                        if ($suspendIfRequired) {
                            Write-Output "$vmName is powered on and has a snapshot, and will be suspended during the move"
                            $suspend = $true
                        } else {
                            Write-Output "Error: $vmName is powered on and has a snapshot, but will not be suspended, process aborted"
                            $suspend = $false
                            exit 2
                        }
                    }
                } else {
                    $suspend = $false
                    $hasSnapshot = False
                    Write-Output "No snapshots currently found for $vmName"

                }

                if ($suspend) {
                    Write-Output "Suspending $vmName"
                    Suspend-VM -vm $vm -confirm:$false
                }

                Write-Output "Moving $vmName to $datastoreName"
                Move-VM -vm $vmName -datastore $datastoreName

                if ($suspend) {
                    Write-Output "Bringing $vmName out of suspension"
                    Start-VM -vm $vm -confirm:$false
                }
                Write-Output "$vmName migrated to $datastoreName"

            } else {
                Write-Output "The datastore only has $freeSpace MB available, but the VM has disks totalling $vmSizeMB MB"
            }
        } else {
            Write-Output "Error: datastore $datastoreName not found"
        }
    } else {
        if ($vm -is [object[]])
        {
            Write-Output "Multiple objects returned for $vmname, please specify a single VM"
        } else {
            write-output "VM Not found - $vmName"
        }
    }
} else {
    write-output "ERROR: VI server not found - $viServer"
}

Write-Output (Get-Date -format "dd/MM/yyyy HH:mm:ss")

Disconnect-VIServer -confirm:$false
exit 0


Wayne's World of IT (WWoIT), Copyright 2009 Wayne Martin. 


Read more!

Wednesday, July 1, 2009

PowerShell - vSphere VMKernel NIC MTU

I came across a problem with VI4 (vSphere) on ESXi, where I was unable to create a vmkernel NIC for iSCSI with the vicfg-vmknic.pl script to set a custom MTU of 9000. This post provides a method using the GUI/perl script, and a PowerShell vSphere PowerCLI script to modify an existing VMKernel NIC.

It seems there is a bug in the perl script doesn't allow you to create a VMKernel NIC, however it does work if you create one through the GUI, and then use the script to delete and re-create the virtual NIC.

This was using a standalone ESXi server with a normal vSwitch - it wasn't managed by VC and it wasn't a DVS.

The GUI/PL script method:

  1. Create a VMKernel NIC in an existing vSwitch through the VI Client, with a port group called iSCSI1 in this example.
  2. Using the CLI, run the following commands, the first to delete the existing NIC, and then another to re-create the same NIC, but with an increased MTU:
    vicfg-vmknic.pl -d iSCSI1
    vicfg-vmknic.pl -a -i x.x.x.x -n x.x.x.x -m 9000 iSCSI1
  3. Run 'vicfg-vmknic.pl –l' to list the VMK NICs, which should show the adjusted MTU

I wasn't particularly happy with this method, so I looked at using the vSphere PowerCLI to set the MTU. For whatever reason, VMware chose not to expose the MTU property, which leaves only the SDK.

Luckily the PowerCLI makes it much easier to use the .Net SDK than previously possible with the Get-View cmdlet, so the script below is a combination of standard PowerCLI with a hook into the vNIC collection and the UpdateVirtualNic method to change a virtual NIC spec.


param (

    $MTU = 9000, 
    $nicName = vmk1, 
    $vcServer = "vcServer", 
    $vmServerName = "esx01"
)

#
#
# Description:
#  Update the MTU of a vmkernel NIC used for iSCSI
#
# Limitations:
#  DVS untested
#
# Assumptions, this script works on the assumption that:
#  The caller provides credentials with permissions to change the specified NIC
#
# Arguments:
#  MTU, The new MTU size, eg. 9000
#  nicName, The vmkernel NIC, eg. vmk1.  Note that this differs from the port group (eg. iSCSI1, with a NIC name of vmk1)
#  vcServer, the VC instance to connect to, eg. vcServer
#  vmServerName, The server name controlled by the VC instance (or direct), eg. esx01
#
# Usage:
#  PowerShell . .\UpdateVMKMTU.ps1 -MTU 9000 -nicName 'vmk1' -vcServer 'vcServer' -vmServerName 'esx01'
#
# References:
#  http://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.host.VirtualNic.Specification.html
#
# Changes:
#  26/06/2009, Wayne Martin, initial version

#$ErrorActionPreference = "Continue"


Connect-VIServer -server $vcServer        # connect to VC
$hostSystem = get-view -ViewType HostSystem -Filter @{"Name" = $vmServerName}   # Find the .Net view of the specified host
$hostConfigManager = $hostSystem.get_ConfigManager()      # Get the config manager
$hostNetworkSystem = $hostConfigManager.get_NetworkSystem()     # Find the MOR of the host network system
$netSystem = Get-View $hostNetworkSystem       # Get the object from the reference for the update method later

$hostconfig = $hostSystem.Config        # Get the current host config
$hostNetwork = $hostconfig.Network        # Get the current network host config
$hostvNIC = $hostNetwork.vNic         # Get the virtual NICs

$nicBeingUpdated = $null
foreach ($hostVirtualNIC in $hostvNIC) {       # For each virtual NIC
    if ($hostVirtualNIC.Device -eq $nicName) {        # Is this the device specified?
        $nicBeingUpdated = $hostVirtualNIC       # Yes, copy the object
    }
}

if ($nicBeingUpdated) {          # Was something found?
    $nicSpec = $nicBeingUpdated.Spec        # Yes, get the current spec
    $currentMTU = $nicSpec.MTU         # Get the current MTU from the spec

    if ($currentMTU -ne $MTU) {         # Is the MTU different?
        $nicSpec.set_Mtu($MTU)         # Yes, update the MTU on the copy of the spec of the existing NIC
        $netSystem.UpdateVirtualNic($nicName, $nicSpec)             # Call the update method from the netsystem, to update the NIC with the modified device spec

        write-output "MTU for $nicName updated from $currentMTU to $MTU"
        (get-vmhostnetwork).VirtualNic        # Output updated information
    } else {
        write-output "MTU for $nicName already set to $MTU, no changes made"
    }

} else {

    write-output "NIC $nicName not found, no changes made"

}


Wayne's World of IT (WWoIT), Copyright 2009 Wayne Martin. 


Read more!

Thursday, December 11, 2008

Useful PowerShell command-line operations

The commands below are a small but growing list of powershell one-liners I've used (and when I remember to take note). Note that this may not be the best and almost certainly isn't the only way in most cases.

Each command-line can be copied and pasted at a PowerShell command prompt, or you can use the commands as part of a PS1 script file if you prefer.


Show help on commands such as 'if', regular expressions etc
help about*

Show help about about_Automatic_variables such as $_ $DebugPreference $OFS
help about_Automatic_variables

Run a command and pipe the results to a variable
cscript.exe //nologo script.wsf | Set-Variable -name scriptResults

Run a command and set a variable with the output
$scriptResults = cscript.exe //nologo script.wsf

Run a command and set a variable with the output using the call operator
$scriptResults = & cscript.exe //nologo script.wsf

Filter an object based on an array of strings
$array = "a", "b"; write-output a b c d | select-string -pattern $array -simpleMatch

Reformat today's date to YYYYMMDD
$today = [DateTime]::Now.ToString("yyyyMMdd")

Find local disks except C: using powershell and output to CSV
Get-WmiObject -Namespace root\cimv2 -ComputerName %server% -Query "SELECT * from Win32_LogicalDisk WHERE FileSystem='NTFS' AND Description = 'Local Fixed Disk' AND Name != 'C:'" | export-csv c:\disk.csv

Use the VI Toolkit Powershell snap-in to query for snapshots
Get-VM | Get-Snapshot | export-csv -path c:\temp\VMsnapshots.csv

Use the VI Toolkit Powershell snap-in to query for snapshot information
Get-VM | Get-Snapshot | foreach-object {$out= $_.VM.Name + "," + $_.Name + "," + $_.Description + "," + $_.PowerState; $out}

Start a remote process using Powershell/WMI
$computer = "."; ([WMICLASS]"\\$computer\root\CIMv2:win32_process").Create("notepad.exe")

Find remote processes and the command-line parameters with PowerShell
Get-WmiObject win32_process | Format-Table ExecutablePath,Caption,CommandLine,CreationDate,WorkingSetSize,ProcessId

Generate a password 10 characters in length, at least 5 punctuation marks
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Web") ; [System.Web.Security.Membership]::GeneratePassword(10,5)

Format a date to XML XSD:dateTime
$Now = [DateTime]::Now.ToString("yyyy-MM-ddTHH:mm:ss")

Check if the given variable is an object
if ($variable -is [object]) {}

Check which network connections (drive mappings) a 2003 computer has
get-WMIObject -computerName "$env:computername" -class win32_logicaldisk -property ProviderName, DeviceID -filter "DriveType=4" | Format-List -property ProviderName, DeviceID

See whether an IP can be resolved to a host name
[System.net.Dns]::GetHostEntry($IP)

See whether a host name can be resolve to an IP
$ip = & { trap {continue}; [System.net.Dns]::GetHostAddresses($computer) }

View the datastore information for the available storage
get-vc -server $server ; Get-Datastore

Check if a file or path exists or not
test-path -path $path ; if (!(test-path -path $path)) {write-output "$path does not exist"}

Use psexec to run a powershell command remotely and report to stdout (eg query 'remote' event logs in this example)
psexec \\machine /s cmd /c "echo. | powershell . get-eventlog -newest 5 -logname application | select message"



Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.


Read more!

Sunday, November 2, 2008

OpsMgr 2007 performance script - VMware datastores

This post provides a method of collecting VirtualCenter datastore information for ESX hosts in one or more clusters as performance data in Operations Manager 2007. You can then trend datastore usage for longer term analysis, and alert on the data for proactive problem management. This is my first attempt at using script to gather performance data in OpsMgr 2007, and my lack of OpsMgr knowledge combined with the limited documentation/examples made this difficult and possibly harder than it needs to be.

Background

In OpsMgr 2007, I thought it would be useful to report on LUN disk space on the ESX side of things in a similar fashion to agent-based Windows disk performance counters, and while this sounds quite easy, I couldn’t find a simple method because:

  1. ESX doesn’t seem to support an SNMP trap to report on VMFS volume capacity/free space. You could write a shell script and add a cron job on the Linux ESX service console, but this would be targeted at each ESX server, and the results would then be duplicated (and wouldn't work with ESX 3i)
  2. VirtualCenter doesn’t appear to support a custom alert based on storage usage, only storage transfer rates.
  3. The ESX servers are only in OpsMgr as network devices, not true agents, so we can’t use WBEM or any other method to query directly. If the upcoming OpsMgr 2007 cross platform extensions work on ESX, this may be easier, although as with option 1 this would still return duplicate datastore information for each ESX server in a cluster and wouldn't work with ESX 3i)

This left a few options to gather the data in OpsMgr:

  1. Query the VMFS storage through the VI snap-in and a PowerShell Script.
  2. Query the VC database directly for this information from a VBS or PowerShell script.
  3. Create a DTS job on the VC SQL server to extract the data to a suitable format.

Options two and three above would not be supported by VMware and may change as the database structure changes between VC versions.

Solution

Querying the VMFS storage from PowerShell and collect the information with VBScript, which includes:

  1. The PowerShell script using the VI snap-in to extract the datastore information
  2. The VBScript to gather the information as performance data in Operations Manager 2007
  3. A trigger to execute the powershell script and put the data in a file to be gathered.
  4. Rules in OpsMgr to gather the data using the type 'Script (Performance)'.

Steps taken:

  1. Install the VMware VI PowerShell snap-in to your Virtual Centre box (you may need to install PowerShell first). VMware-Vim4PS-1.0.0-113525.exe is the binary I used.
  2. Create an AD group and user for rights to VC. Add the user to the group.
  3. Set rights in VirtualCenter to allow the group read-only rights at the root. This should be more granular if possible.
  4. Add a recurring trigger on your VC box to run the PowerShell script to extract the information. Eg, this could be a scheduled task that runs a batch file or directly executes:
    1. powershell . ".\PerfGatherVMwareDSSpace.ps1"
  5. Create two rules in Operations Manager, Script (Performance) to gather the daily log file and store in the database as performance data. One rule is for the free space, another is for the free space as a percentage of the capacity. See the performance mapper notes below.
    1. The rules were created under ‘Windows Server 2003 Computer’, not enabled by default and overridden for the VC server (where the powershell script runs). You may want to target the rule differently, depending on your environment.
  6. Create a performance view, showing performance data collected by the two rules above.
  7. Create a monitor, using the LogicalDisk ‘% Disk Free’ object and counter to monitor on a threshold of 10%. Similarly, the monitor could be created under ‘Windows Server 2003 Computer’, not enabled by default and overridden for the VC server. The alert contains the following information:
    1. Object: $Data/Context/ObjectName$, Counter: $Data/Context/CounterName$, Instance: $Data/Context/InstanceName$, Value: $Data/Context/Value$

Note that when using the script performance data provider, the ‘Performance Mapper’ tab is used to map the data returned by the script to the database. To make this generic, both the instance name and the value are used from each element:

  • Object: LogicalDisk
  • Counter: Free Megabytes
  • Instance: $Data/Property/@Name$
  • Value: $Data/Property[@Name]$

Note that the VBScript creates three typed ‘property bags’ and returns all. This results in a single XML that contains three dataitem elements, one for each instance. Creating a single property bag and adding the three instances results in only the first being processed.

This should now gather free space in MB and as a percentage of the size for all VMFS datastores your VC server knows about, visible through the performance view, and alerted on when free space is less than 10% of the capacity for each volume.

SQL Queries

Some SQL queries against the OperationsManagerDW database I used along the way to query from the datawarehouse that the data was being gathered. Note that where I've specified 'datastore%' as a filter, you'll need to change this to the prefix of your datastores, eg ds01, ds02 would be ds%.

/*Return the raw performance data collected for the datastore* instances: */

select DateAdd(Hour, 10, DateTime) as DateTime, InstanceName, SampleValue from perf.vperfRaw
inner join vPerformanceRuleInstance on perf.vperfRaw.PerformanceRuleInstanceRowID = vPerformanceRuleInstance.PerformanceRuleInstanceRowID
where InstanceName like 'datastore%'
order by datetime desc

/* Find new rules: */
select top 10 * from dbo.vPerformanceRuleInstance
where instancename like 'datastore%'
order by performanceruleinstancerowid desc

/* Find new raw performance data: */

select top 100 DateTime, SampleValue, ObjectName, CounterName, FullName, Path, Name, DisplayName, ManagedEntityDefaultName from perf.vperfraw
inner join vPerformanceRule on perf.vperfraw.PerformanceRuleInstanceRowID = vPerformanceRule.RulerowID
inner join vManagedEntity on perf.vperfraw.ManagedEntityRowID = vManagedEntity.ManagedEntityRowID
order by datetime desc

/* Find new rule instances that have been created: */

select top 10 * from dbo.vPerformanceRuleInstance
order by performanceruleinstancerowid desc
--


#PowerShell Script - PerfGatherVMwareDSSpace.ps1
# Note that this includes a context to connect to VC, with hardcoded username and password.  You could avoid this by running the scheduled task under the security context you created with inherent rights to VC, and then remove the explicit -credential argument to Connect-VIServer.

$ErrorActionPreference = "SilentlyContinue"
add-pssnapin VMware.VimAutomation.Core

$ADMINLOG = "c:\admin\logs"
$outputFile = ""
$today = [DateTime]::Now.ToString("yyyyMMdd")

$scriptName = $MyInvocation.MyCommand.Name
$scriptName = $scriptname.substring(0, $scriptname.LastIndexOf("."))

if ($env:adminlog -ne $null) {        # Was there an adminlog environment variable?
    $outputFile = $env:adminlog
} else {
   $outputFile = $ADMINLOG        # No, use the constant default  
}

$outputFile += "\" + $scriptname + "_" + $today + ".csv"    # Construct the full path to the output file 

$server = "vc01"          # VC instance
$username = "domain\user"        # Hardcoding is bad, but at least this is a RO account.
$password = "password"

$pwd = convertto-securestring $password -asplaintext -force

$cred = New-Object Management.Automation.PSCredential ($username, $pwd)   # Create the credentials to use with the VC connection

$vmserver = & { trap {continue}; Connect-VIServer -server $server -Credential $cred } # Connect to VC, trapping the error

if ($vmserver -is [System.Object]) {       # Do we have a connection?
    Get-Datastore -server $vmserver | export-csv -noTypeInformation -path $outputFile # Yes, get the datastore details and store as CSV
    if (test-path -path $outputFile) {
        write-output "Datastore details exported to $outputFile"
    } else {
        write-output "Error: Datastore details were not exported to $outputFile"
    }
    $vmserver = $null
} else {
    write-output "Could not connect to $server"
}


#------



' VBScript


' Parse a CSV file containing the VMware volume information, and return the specified field from the data file or the calculated percent free

Const TOKEN_DATE = "%date%"
Dim SOURCE_FILE : SOURCE_FILE = "c:\admin\logs\PerfGatherVMwareDSSpace_" & TOKEN_DATE & ".csv"  ' Today's log file exported from the PowerShell VI-snapin script

Const DELIMITER = ","           ' CSV data file
Const ForReading = 1
Const DISKTYPE_VMFS = "VMFS"          ' We're interested only in lines with VMFS volumes

Const QUERY_SIZE = "Size"
Const QUERY_FREE = "Free"
QUERY_PERCENT = "Percent"

Dim QUERY_DEFAULT : QUERY_DEFAULT = QUERY_FREE 

Const FIELD_SIZE = 1           ' The field in the CSV that contains the capacity of the VMFS volume
Const FIELD_FREE = 0           ' The field in the CSV that contains the free disk space on the VMFS volume
Const FIELD_PERCENT = 2

Const PerfDataType  = 2

Set oFSO = CreateObject("Scripting.FileSystemObject")

Main()

wscript.quit(0)

Sub Main()

 If WScript.Arguments.UnNamed.Count >= 1 Then
  strQueryType = WScript.Arguments.UnNamed(0)
  wscript.echo "Command-line argument passed, querying for " & strQueryType
 Else
  strQueryType = QUERY_DEFAULT
  wscript.echo "No command-line argument passed, querying for the default of " & strQueryType
 End if

 Select Case strQueryType
  Case QUERY_SIZE  strField = FIELD_SIZE 
  Case QUERY_FREE  strField = FIELD_FREE
  Case QUERY_PERCENT strField = QUERY_PERCENT
  Case Else  strField = FIELD_FREE
 End Select

 dtmDate = Now 
 strToday = DatePart("yyyy", dtmDate)         ' YYYY
 strToday = strToday & String(2 - Len(DatePart("m", dtmDate)),"0") & DatePart("m", dtmDate) ' MM
 strToday = strToday & String(2 - Len(DatePart("d", dtmDate)),"0") & DatePart("d", dtmDate) ' DD

 strDataFile = Replace(SOURCE_FILE, TOKEN_DATE, strToday)     ' Build the path to the data file
 wscript.echo "Looking for " & strDataFile

 Dim oAPI, oBag
 Set oAPI = CreateObject("MOM.ScriptAPI")

 If oFSO.FileExists(strDataFile) Then        ' Does the data file exist for today?
  WScript.Echo "Log file found, continuing"
  Call ReadFile(strDataFile, strBuffer)       ' Yes, read the contents of the file into the buffer

  For Each strLine in Split(strBuffer, vbCRLF)      ' For each line
   arrEntry = Split(strLine, DELIMITER)      ' Split the line into an array on comma
   If UBound(arrEntry) = 5 Then       ' Did this line have a valid number of fields?
    If strComp(arrEntry(3), DISKTYPE_VMFS) = 0 Then    ' Yes, is it a VMFS volume? (excludes the header)
    wscript.echo "Processing " & strLine
     If IsNumeric(arrEntry(FIELD_FREE)) AND IsNumeric(arrEntry(FIELD_SIZE)) Then     ' Yes, is the field we're after a numeric?
      Set oBag = oAPI.CreateTypedPropertyBag(PerfDataType)  Create a typed name/value pair property bag

      If (strField = FIELD_SIZE) OR (strField = FIELD_FREE ) Then
       Call oBag.AddValue(arrEntry(5),CLng(arrEntry(strField))) ' Yes, convert to a long and add to the bag
      ElseIf strField = QUERY_PERCENT Then
       dblFreePercent = CDbl(arrEntry(FIELD_FREE) / arrEntry(FIELD_SIZE)*100)
       Call oBag.AddValue(arrEntry(5), Round(dblFreePercent, 2))
      End If
      oAPI.AddItem(oBag)     ' Add the item to the bag (doing it here results in three datatime elements)

     End If  
    End If
   End If
  Next

 Else
  WScript.Echo "Error: Daily data file not found - " & strDataFile 

 End If

 Call oAPI.ReturnItems 

End Sub    


'********************************************************
' Purpose: Read a file and store the contents in the buffer
' Assumptions: oFSO exists
'              strLogFile contains the path/filename of the file to operate on
' Effects: strBuffer is by reference
' Inputs: strFileName, Path and filename of the source file
'   strBuffer, the buffer used to store the contents of the file
' Returns: None
'
'********************************************************
Sub ReadFile (ByVal strFileName, ByRef strBuffer)   
 On Error Resume Next
 Dim objTextStream

 If Not oFSO.FileExists(strFileName) Then
  WScript.Echo "Error: " & strFileName & " file not found."
  Exit Sub
 End If
    Set objTextStream = oFSO.OpenTextFile(strFileName, ForReading)
 strBuffer = objTextStream.ReadAll
End Sub

'------

References:

How to Create a Probe-Based Performance Collection Rule in Operations Manager 2007
http://technet.microsoft.com/en-us/library/bb381406.aspx

MOMScriptAPI.CreatePropertyBag Method
http://msdn.microsoft.com/en-us/library/bb437556.aspx

XPath Examples:
http://msdn.microsoft.com/en-us/library/ms256086(VS.85).aspx

http://blogs.msdn.com/mariussutara/archive/2008/01/24/momscriptapi-createtypedpropertybag-method.aspx

http://blogs.technet.com/kevinholman/archive/2008/07/02/collecting-and-monitoring-information-from-wmi-as-performance-data.aspx

http://www.afinn.net/2008/07/collecting-performance-data-in-operations-manager-2007-and-publishing-to-sharepoint-part-1/

http://blogs.msdn.com/mariussutara/archive/2007/11/13/alert-description-and-parameter-replacement.aspx

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.


Read more!

All Posts

printQueue AD objects for 2003 ClusterVirtualCenter Physical to VirtualVirtual 2003 MSCS Cluster in ESX VI3
Finding duplicate DNS recordsCommand-line automation – Echo and macrosCommand-line automation – set
Command-line automation - errorlevels and ifCommand-line automation - find and findstrBuilding blocks of command-line automation - FOR
Useful PowerShell command-line operationsMSCS 2003 Cluster Virtual Server ComponentsServer-side process for simple file access
OpsMgr 2007 performance script - VMware datastores...Enumerating URLs in Internet ExplorerNTLM Trusts between 2003 and NT4
2003 Servers with Hibernation enabledReading Shortcuts with PowerShell and VBSModifying DLL Resources
Automatically mapping printersSimple string encryption with PowerShellUseful NTFS and security command-line operations
Useful Windows Printer command-line operationsUseful Windows MSCS Cluster command-line operation...Useful VMware ESX and VC command-line operations
Useful general command-line operationsUseful DNS, DHCP and WINS command-line operationsUseful Active Directory command-line operations
Useful command-linesCreating secedit templates with PowerShellFixing Permissions with NTFS intra-volume moves
Converting filetime with vbs and PowerShellDifference between bat and cmdReplica Domain for Authentication
Troubleshooting Windows PrintingRenaming a user account in ADOpsMgr 2007 Reports - Sorting, Filtering, Charting...
WMIC XSL CSV output formattingEnumerating File Server ResourcesWMIC Custom Alias and Format
AD site discoveryPassing Parameters between OpsMgr and SSRSAnalyzing Windows Kernel Dumps
Process list with command-line argumentsOpsMgr 2007 Customized Reporting - SQL QueriesPreventing accidental NTFS data moves
FSRM and NTFS Quotas in 2003 R2PowerShell Deleting NTFS Alternate Data StreamsNTFS links - reparse, symbolic, hard, junction
IE Warnings when files are executedPowerShell Low-level keyboard hookCross-forest authentication and GP processing
Deleting Invalid SMS 2003 Distribution PointsCross-forest authentication and site synchronizati...Determining AD attribute replication
AD Security vs Distribution GroupsTroubleshooting cross-forest trust secure channels...RIS cross-domain access
Large SMS Web Reports return Error 500Troubleshooting SMS 2003 MP and SLPRemotely determine physical memory
VMware SDK with PowershellSpinning Excel Pie ChartPoke-Info PowerShell script
Reading web content with PowerShellAutomated Cluster File Security and PurgingManaging printers at the command-line
File System Filters and minifiltersOpsMgr 2007 SSRS Reports using SQL 2005 XMLAccess Based Enumeration in 2003 and MSCS
Find VM snapshots in ESX/VCComparing MSCS/VMware/DFS File & PrintModifying Exchange mailbox permissions
Nested 'for /f' catch-allPowerShell FindFirstFileW bypassing MAX_PATHRunning PowerSell Scripts from ASP.Net
Binary <-> Hex String files with PowershellOpsMgr 2007 Current Performance InstancesImpersonating a user without passwords
Running a process in the secure winlogon desktopShadow an XP Terminal Services sessionFind where a user is logged on from
Active Directory _msdcs DNS zonesUnlocking XP/2003 without passwords2003 Cluster-enabled scheduled tasks
Purging aged files from the filesystemFinding customised ADM templates in ADDomain local security groups for cross-forest secu...
Account Management eventlog auditingVMware cluster/Virtual Center StatisticsRunning scheduled tasks as a non-administrator
Audit Windows 2003 print server usageActive Directory DiagnosticsViewing NTFS information with nfi and diskedit
Performance Tuning for 2003 File ServersChecking ESX/VC VMs for snapshotsShowing non-persistent devices in device manager
Implementing an MSCS 2003 server clusterFinding users on a subnetWMI filter for subnet filtered Group Policy
Testing DNS records for scavengingRefreshing Computer Account AD Group MembershipTesting Network Ports from Windows
Using Recovery Console with RISPAE Boot.ini Switch for DEP or 4GB+ memoryUsing 32-bit COM objects on x64 platforms
Active Directory Organizational Unit (OU) DesignTroubleshooting computer accounts in an Active Dir...260+ character MAX_PATH limitations in filenames
Create or modify a security template for NTFS perm...Find where a user is connecting from through WMISDDL syntax in secedit security templates

About Me

I’ve worked in IT for over 20 years, and I know just about enough to realise that I don’t know very much.