Michael's profileThe Old Dogs Scripting B...BlogListsNetworkMore ![]() | Help |
|
|
August 13 Script EditorsI see a lot of people asking about script editors for vbScript and Powershell.If you want free then I would suggest the following:Notepad Plus is a good replacement for Notepad. http://notepad-plus.sourceforge.net/uk/site.htm Here are the features of Notepad++ : Syntax Highlighting and Syntax Folding
C C++ Java C# XML HTML PHP CSS makefile ASCII art (.nfo) doxygen ini file batch file Javascript ASP VB/VBS SQL Objective-C RC resource file Pascal Perl Python Lua TeX TCL Assembler Ruby Lisp Scheme Properties Diff Smalltalk Postscript VHDL Ada Caml AutoIt KiXtart Matlab Verilog Haskell InnoSetup CMake YAML
User Defined Syntax Highlighting Auto-completion Multi-Document Multi-View Regular Expression Search/Replace supported Full Drag ‘N' Drop supported Dynamic position of Views File Status Auto-detection Zoom in and zoom out Multi-Language environment supported Bookmark Brace and Indent guideline Highlighting Macro recording and playback Or you could go with Sapiens PrimalPad: SAPIEN Technologies has released a new FREE (for personal use) portable editor called PrimalPad.
With PrimalPad you can edit PowerShell, VBScript, JScript and HTML files.
The editor also support multi-tabbed environment, line numbers and color coding.
The editor is a single EXE file, no need to install anything, just double click it and start editing your scripts.
PrimalPad is available as x32 and x64 versions and can be downloaded HERE (registration needed).
If you are only working in Powershell then:the Power Gui from PowerGui.org is a good choice and it’s free: http://powergui.org/index.jspa For a few dollars more; Try EditPlushttp://www.editplus.com/register.html I like it because it lets me work in columns and it will find and replace tabs and line feeds.
It supports the basic scripting languages, but last I looked it did not directly support Powershell.
1-user license: US $35
One just for vbScript is vbsEdit;This does amazing things in vbScript, including showing you all the functions and methods
available when you open up a com object. I really like this feature when I am scripting Excel.
VbsEdit Single-user license for $49.00
Of course if you want the very best and are willing to pay for it then you must buy Primal Script.Price: $299.00
I won’t go into details, but if scripting is you job, then this is the one for you.
Open Command Window HereI ran across this article (Open Command Window Here « Burgaud.com) about adding the Open Command Line Here to the Right Click menu in Explorer. It’s a good article and it shows you how to do the job with some registry hacks. I have included my versions here: Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\shell\CommandPrompt] Just copy the above and save it to a regfile. I call mine open_cmd_here.reg Then open up explorer and double click the file. It will ask you if you want to add this to your registry, say yes and then close Explorer. Next time you open Explorer and right click on a Directory, you will have two options. 1. open a command prompt as an admin 2. as a regular user. And here is one to add Powershell: Windows Registry Editor Version 5.00
August 12 Powershell InStr() and Mid()First of all, PowerShell does not have an InStr() or Mid() function, at least version one does not. I was trying to extract the Group name from the Common Name in AD and I wanted everything between the first = sign and the first "," comma. (I know there are other ways) So I found that in Powershell you can get the first position of a character by using indexof() like so: $a=$path $b = $a.indexof("=") # so the first = is at character 9 I don't want the equal sign so I add 1 $b = $b + 1 Next I get the index of the first comma. $c = $a.indexof(",") Then I get the number of characters between $c and $b $d = $c - $b Finally I get the substring I wanted: substring starts at the first character, in this case $b and then includes the number of characters in your substring, in this case $d. $e = $a.substring($b,$d) And here is my MID function: Function MID($path) { $a=$path $b = $a.indexof("=") $b = $b + 1 $c = $a.indexof(",") $d = $c - $b $e = $a.substring($b,$d) $ws.Cells.Item($row,4) = $e #This puts my substring in an Excel Spreadsheet in column 4 } August 07 Error trapping and EXE's in vbScriptI was asked to make up a menu for some connection testing. Many of the machines that are being tested are Windows XP and many of the testers are using XP. I found that Net View works differently on XP than it does on Vista, So I had to come up with a way to determine if the C$ share was available on either XP or Vista machines and from either a Vista or XP machine. I attempt to map to the test machine's C$ share and record the results in a text file. I then read the file and pull out the error code. Here is the function: Function View(strComputer) On Error Resume Next Const OpenAsDefault = -2 Const FailIfNotExist = 0 Const ForReading = 1 Set oShell = CreateObject("WScript.Shell") Set oFSO = CreateObject("Scripting.FileSystemObject") sTemp = oShell.ExpandEnvironmentStrings("%TEMP%") sTempFile = sTemp & "\runresult.tmp" oShell.Run "%comspec% /c net use Z: \\" & strComputer & "\C$ " & ">" & sTempFile & " 2>&1", 0 , True Set fFile = oFSO.OpenTextFile(sTempFile, ForReading, _ FailIfNotExist, OpenAsDefault) sResults = fFile.ReadAll fFile.Close oShell.Run "%comspec% /c net use Z: /Delete >" & sTempFile & " 2>&1", 0 , True oFSO.DeleteFile(sTempFile) If CBool(InStr(sResults, "successfully."))then statval.value = strComputer & " C$ Is Available" ElseIf CBool(InStr(sResults, "2")) Then statval.value = strComputer & " File not found" ElseIf CBool(InStr(sResults, "3")) Then statval.value = strComputer & " Path not found." ElseIf CBool(InStr(sResults, "4")) Then statval.value = strComputer & " Too many files open." ElseIf CBool(InStr(sResults, "5")) Then statval.value = strComputer & " Access denied." Else statval.value = statval.value & VbCrLf & strComputer & " C$ Is Not Shared" End If ' DOS 2=File not found. ' DOS 3=Path not found. ' DOS 4=Too many files open. ' DOS 5=Access denied. End Function The 2>&1 puts the standard error in the standard out pipe so everything is in one file. Oh and the function is part of an HTA which is why it writes out to statval.value. You might recognize the construction from Alex K. Angelopoulos great ping function. August 04 Adding Color to Excel the Powershell WayWant to add a little color to your spreadsheets?
This will show you how and create a sample spreadsheet with the 56 colors available to you.
###### Start Posh Script ########
$xl = new-object -comobject excel.application
$xl.Visible = $true $xl.DisplayAlerts = $False $wb = $xl.Workbooks.Add()
$ws = $wb.Worksheets.Item(1) $row = 1 $i = 1 For($i = 1; $i -lt 56; $i++){
$ws.Cells.Item($row, 1) = "'$'ws.Cells.Item($row, 2).Font.ColorIndex = " + $row #<-- row 10 $ws.Cells.Item($row, 2).Font.ColorIndex = $row $ws.Cells.Item($row, 2) = "test " + $row $row++ } [void]$ws.cells.entireColumn.Autofit()
############# End POSH Script ############### Try taking the ' out from around the $ in row 10 and see what you get.. Be sure you do if you want to just cut and paste the cell. July 29 Run PowerShell Scripts from an HTA MenuHere is a simple menu to run a dozen different PowerShell Scripts;
Just the thing for people who need to run a bunch of canned scripts on a regular basis.
Copy this and save it as HTA type file. eg. MyMenu.hta
Be carefull of the line wraps. Everything the between the <BR>'s is one line
<!--
'========================================================================== ' ' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 2007 ' ' NAME: MyMenu.hta ' ' AUTHOR: OldDog '
' DATE : 7/29/2009
' ' COMMENT: Use Short Path Names!!!!!! You find them by doing a DIR /x ' '========================================================================== --> <HTML>
<HEAD> <TITLE>MyMenu.hta</TITLE> <script LANGUAGE="vbscript"> Sub Window_Onload self.Focus() self.ResizeTo 900,700 End Sub Sub runhta(htaSrc) Dim WshSHell set WshShell = CreateObject("WScript.Shell") WshShell.Run(htaSrc) End Sub </script>
</HEAD> <BODY STYLE="font:14 pt arial; color:white; filter:progid:DXImageTransform.Microsoft.Gradient (GradientType=1, StartColorStr='#000000', EndColorStr='#0000FF')"> </style> <CENTER><H2>My Menu</H2></CENTER> <HR> <OL> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\FCTopology Locater.ps1')">FCTopology Locater</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\FC_4Gb_Drive_check.ps1')">FC_4Gb_Drive_check</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\Remote_Ports_check.ps1')">Remote_Ports_check</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\Connection_Counter_check.ps1')">Connection_Counter_check</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\SubSystem_Table.ps1')">SubSystem_Table</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\Soft_Reset_check.ps1')">Total_Soft_Reset_check</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\Rogue_Events.ps1')">Rogue_Events</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\Maximum-Values.ps1')">Maximum-Values</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\Maxtor_Drives_Search.ps1')">Maxtor_Drives_Search</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\QLA4010 HBA_Locator.ps1')">QLA4010 HBA_Locator</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\Raid_Device_and_Extent_Counter.ps1')">Raid_Device_and_Extent_Counter</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\Search_for_SATA_revisions.ps1')">Search_for_SATA_revisions</a> <BR> <LI><a href="#" onClick="runhta('powershell -noexit C:\PROGRA~1\PhoneH~1\System_Upgrade_Dates.ps1')">System_Upgrade_Dates.ps1</a> <BR> </OL> <BR><BR><BR> <CENTER><BUTTON onclick="self.close()">Exit</BUTTON></CENTER> <BR> </BODY> </HTML> July 27 Powershell Menu and AD SearchI thought my script to find nested groups in AD would be even better if I could find the group and then search for it using a menu or a form.
Because there was a fixed number of groups marketing was looking for I went with a menu.
I found the menu here: http://mspowershell.blogspot.com/2009/02/cli-menu-in-powershell.html
#### Begin Script ######
function get-dn($SAMName)
{ $root = [ADSI]'' $searcher = New-Object System.DirectoryServices.DirectorySearcher($root) $searcher.filter = "(&(objectClass=*)(cn= $SAMName*))" #<-- This is the CN or common name $user = $searcher.findall() if ($user.count -gt 1) { $count = 0 foreach($i in $user) { Write-Host $count ": " $i.path $count = $count + 1 } $selection = Read-Host "Please select item: " return $user[$selection].path } else { return $user[0].path } } function DrawMenu { ## supportfunction to the Menu function below param ($menuItems, $menuPosition, $menuTitel) $fcolor = $host.UI.RawUI.ForegroundColor $bcolor = $host.UI.RawUI.BackgroundColor $l = $menuItems.length + 1 cls $menuwidth = $menuTitel.length + 4 Write-Host "`t" -NoNewLine Write-Host ("*" * $menuwidth) -fore $fcolor -back $bcolor Write-Host "`t" -NoNewLine Write-Host "* $menuTitel *" -fore $fcolor -back $bcolor Write-Host "`t" -NoNewLine Write-Host ("*" * $menuwidth) -fore $fcolor -back $bcolor Write-Host "" Write-Debug "L: $l MenuItems: $menuItems MenuPosition: $menuposition" for ($i = 0; $i -le $l;$i++) { Write-Host "`t" -NoNewLine if ($i -eq $menuPosition) { Write-Host "$($menuItems[$i])" -fore $bcolor -back $fcolor } else { Write-Host "$($menuItems[$i])" -fore $fcolor -back $bcolor } } } function Menu {
## Generate a small "DOS-like" menu. ## Choose a menuitem using up and down arrows, select by pressing ENTER param ([array]$menuItems, $menuTitel = "MENU") $vkeycode = 0 $pos = 0 DrawMenu $menuItems $pos $menuTitel While ($vkeycode -ne 13) { $press = $host.ui.rawui.readkey("NoEcho,IncludeKeyDown") $vkeycode = $press.virtualkeycode Write-host "$($press.character)" -NoNewLine If ($vkeycode -eq 38) {$pos--} If ($vkeycode -eq 40) {$pos++} if ($pos -lt 0) {$pos = 0} if ($pos -ge $menuItems.length) {$pos = $menuItems.length -1} DrawMenu $menuItems $pos $menuTitel } Write-Output $($menuItems[$pos]) } function get-NestedMembers ($group){
if ($group.objectclass[1] -eq 'group') { write-verbose "Group $($group.cn)" $Group.member |% { $de = new-object directoryservices.directoryentry("LDAP://$_") if ($de.objectclass[1] -eq 'group') { get-NestedMembers $de } Else { $ws.cells.item($row,1) = $de.givenname.tostring() $ws.Cells.Item($row,2) = $de.SN.tostring() $ws.Cells.Item($row,3) = $de.mail.tostring() $row++ } } } Else { Throw "$group is not a group" } } Function xl($path) {
$xlSummaryAbove = 0 $xlSortValues = $xlPinYin = 1 $xlAscending = 1 $xlDescending = 2 $xl = New-Object -comobject excel.application
$xl.Visible = $true $wb = $xl.Workbooks.Add() $ws = $wb.Worksheets.Item(1) $row = 2 $group = New-Object directoryservices.directoryentry($path) $N = $group.Name.tostring() $N = $N.Substring(0,29) $ws.name = $N.tostring() $ws.Cells.Item(1,1) = "First Name" $ws.Cells.Item(1,2) = "Last Name" $ws.Cells.Item(1,3) = "EMail Address" $range = $ws.range("A1:C1") $range.font.bold = "true" # Call the function
get-NestedMembers $group # one-column sort --> works
$range1 = $ws.range("A2:C2500") $range2 = $ws.range("B2") [void]$range1.sort($range2, $xlAscending) [void]$range1.entireColumn.Autofit() } $ErrorActionPreference = 'silentlycontinue' $crdm = " in house marketing"," marketing","marketing support" #<-- Menu items go here
$selection = Menu $crdm "WHAT DO YOU WANNA DO?" $path = get-dn $selection xl($path) get-NestedMembers($path) ##### End Script ######
And a different version in vbScript that uses a HTA menu.
<html>
<head> <title>Get Mail Group Members from AD</title> <HTA:APPLICATION ID="objADSPath-GM" APPLICATIONNAME="Get Mail Group Members" SCROLL="off" SINGLEINSTANCE="yes" > </head> <script language="VBScript"> Sub Window_Onload self.Focus() self.ResizeTo 900,600 End Sub Sub RunScript
On Error Resume Next Const ADS_SCOPE_SUBTREE = 2 xlDown = -4121 xlToRight = -4161 xlSummaryAbove = 0 xlSortValues = xlPinYin = 1 xlAscending = 1 xlDescending = 2 xlTopToBottom = 1 xlGuess = 0 xlDescending = 2 Set objExcel = CreateObject("Excel.Application") objExcel.Visible = True objExcel.DisplayAlerts = False Set objWorkbook = objExcel.Workbooks.add Set objWorksheet = objWorkbook.Worksheets(1) objWorksheet.Range("A1:E1").Font.Bold = True iRow = 2
objWorksheet.Cells.Item(1,1) = "First Name"
objWorksheet.Cells.Item(1,2) = "Last Name" objWorksheet.Cells.Item(1,3) = "EMail Address" objWorksheet.range("A1:C1").font.bold = "true" DNC = GetObject("LDAP://RootDSE").Get("defaultNamingContext") Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command") objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCommand.ActiveConnection = objConnection objCommand.Properties("Page Size") = 1 objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE If AD_Obj(0).Checked Then
ADobj = AD_Obj(0).Value statval.value = ADobj & vbTab End If If AD_Obj(1).Checked Then ADobj = AD_Obj(1).Value statval.value = ADobj & vbTab End If If AD_Obj(2).Checked Then ADobj = AD_Obj(2).Value statval.value = ADobj & vbTab End If If AD_Obj(3).Checked Then ADobj = AD_Obj(3).Value statval.value = ADobj & vbTab End If If AD_Obj(4).Checked Then ADobj = AD_Obj(4).Value statval.value = ADobj & vbTab End If If AD_Obj(5).Checked Then ADobj = AD_Obj(5).Value statval.value = ADobj & vbTab End If If AD_Obj(6).Checked Then ADobj = AD_Obj(6).Value statval.value = ADobj & vbTab End If If AD_Obj(7).Checked Then ADobj = AD_Obj(7).Value statval.value = ADobj & vbTab End If If AD_Obj(8).Checked Then ADobj = AD_Obj(8).Value statval.value = ADobj & vbTab End If If AD_Obj(9).Checked Then ADobj = AD_Obj(9).Value statval.value = ADobj & vbTab End If If AD_Obj(10).Checked Then ADobj = AD_Obj(10).Value statval.value = ADobj & vbTab End If If AD_Obj(11).Checked Then ADobj = AD_Obj(11).Value statval.value = ADobj & vbTab End If If AD_Obj(12).Checked Then ADobj = AD_Obj(12).Value statval.value = ADobj & vbTab End If If AD_Obj(13).Checked Then ADobj = AD_Obj(13).Value statval.value = ADobj & vbTab End If If AD_Obj(14).Checked Then ADobj = AD_Obj(13).Value statval.value = ADobj & vbTab End If If ADobj = "" Then Exit Sub End If objCommand.CommandText = "SELECT Adspath FROM 'LDAP://" & DNC & "'" & _
" WHERE name='" & ADobj & "'" & " OR sAMAccountName='" & ADobj & "'" Set objRecordSet = objCommand.Execute objRecordSet.MoveFirst If Err <> 0 Then
If Err.Number = 3021 Then TxtADSPath.Value = Trim(AD_Obj.Value) & " Not Found!!!" Else TxtADSPath.Value = Err.Number & " " & Err.Description End If txtou.Value = "" Err.Clear Exit Sub End If arrAD = Split(objRecordSet.Fields("AdsPath").Value, ",")
For i = 0 to Ubound(arrAD)
If InStr(arrAD(i), "OU=") Or InStr(arrAD(i), "LDAP://OU=") Then iLength = Len(arrAD(i)) leading = iif(InStr(arrAD(i),"LDAP://OU="),10,3) OULength = iLength - leading OU = Right(arrAD(i), OULength) Exit For End If Next txtou.Value = OU TxtADSPath.Value = objRecordSet.Fields("Adspath").Value
group = TxtADSPath.Value Set objGroup = GetObject(TxtADSPath.Value) Name = mid(objGroup.Name, instr(1,objGroup.Name,"=") + 1 ) 'set Worksheet name to that of the DL objWorksheet.Name = Left(Name, 30)
For Each strUser In objGroup.Member
Set objUser = GetObject("LDAP://" & strUser) objWorksheet.Cells(iRow,1) = objUser.GivenName objWorksheet.Cells(iRow,2) = objUser.SN objWorksheet.Cells(iRow,3) = objUser.mail iRow=iRow + 1 Next Set objRange = objWorksheet.UsedRange Set objRange = objExcel.Range("A2:B2500") Set objRange2 = objExcel.Range("B2") objRange.Sort objRange2,1,,,,,,,1 objWorksheet.Cells.EntireColumn.AutoFit() objRecordSet.MoveNext statval.value = ADobj & VbCrLf & "All Done" End Sub Sub Window_onLoad
window.resizeTo 1024, 768 End Sub Sub ExitHTA
self.close() End Sub Function XL(group)
End Function
Function iif(cond,t,f)
If cond Then iif = t Else iif = f End If End Function </script> <BODY>
<body STYLE="font:14 pt arial; color:white; filter:progid:DXImageTransform.Microsoft.Gradient (GradientType=1, StartColorStr='#000000', EndColorStr='#0000FF')"> <p align="center"> AD Mail Groups<p> <!-- Make sure you have the correct sAMAccountName in the value section. --> <input type="radio" name="AD_Obj" value="in house marketing">In house marketing<br> <input type="radio" name="AD_Obj" value="Sales and Marketing Dirs and VPs">Sales and Marketing Dirs and VPs<br> <input type="radio" name="AD_Obj" value="Marketing support">Marketing support<br> <input type="radio" name="AD_Obj" value="In house marketing">In house marketing<br> <input type="radio" name="AD_Obj" value="US Field Marketing">US Field Marketing<br> <input type="radio" name="AD_Obj" value="Regional Contract Managers">RCM Regional Contract Managers<br> <input type="radio" name="AD_Obj" value="RISA">RISA<br> <input type="radio" name="AD_Obj" value="Corporate Sales">Corporate Sales<br> <input type="radio" name="AD_Obj" value="Field Force Training">Field Force Training<br> <input type="radio" name="AD_Obj" value="Field HR Generalists">Field HR Generalists<br> <input type="radio" name="AD_Obj" value="Marketing">Marketing<br> <input type="radio" name="AD_Obj" value="Sales and Clinical US Distlist">Sales and Clinical US Distlist<br> <input type="radio" name="AD_Obj" value="Marketing DistList">Marketing DistList<br> <input type="radio" name="AD_Obj" value="Entire US Sales Force">Entire US Sales Force<br> <input type="radio" name="AD_Obj" value="Sales and Service Operations">Sales and Service Operations<p> <br> <br> <p align="center"> <input type="button" value="Get Group Members" name="GetGM" onclick="RunScript"><p> <p align="center"> AdsPath <input type="text" name="TxtADSPath" size="145" readonly><p> <p align="center"> OU <input type="text" name="txtOU" size="20" readonly><p> <p align="center"> <textarea rows="2" name="statval" READONLY cols="75" style="text-align:center">Status Box</textarea></p> <p align="center"> <input type="BUTTON" name="button1" value="Exit" onclick=self.close> </BODY> </html> July 24 Get-NestedMembers of an AD groupHere is a script I did for marketing. It pulls a mail group from AD
and outputs the First Name, Last Name and EMail address of all the members
of the group. Thanks to the PowerShell Guy for the function.
#<------- POSH Script ----------------------------------->
function get-NestedMembers ($group){
if ($group.objectclass[1] -eq 'group') { write-verbose "Group $($group.cn)" $Group.member |% { $de = new-object directoryservices.directoryentry("LDAP://$_") if ($de.objectclass[1] -eq 'group') { get-NestedMembers $de } Else { $ws.cells.item($row,1) = $de.givenname.tostring() $ws.Cells.Item($row,2) = $de.SN.tostring() $ws.Cells.Item($row,3) = $de.mail.tostring() $row++ } } } Else { Throw "$group is not a group" } } $xlSummaryAbove = 0 $xlSortValues = $xlPinYin = 1 $xlAscending = 1 $xlDescending = 2 $xl = New-Object -comobject excel.application
$xl.Visible = $true $wb = $xl.Workbooks.Add() $ws = $wb.Worksheets.Item(1) $row = 2 $group = New-Object directoryservices.directoryentry("LDAP:// cn=MailGroup,OU=Groups,DC=mow,DC=Local") $N = $group.Name.tostring() $N = $N.Substring(0,29) $ws.name = $N.tostring() $ws.Cells.Item(1,1) = "First Name"
$ws.Cells.Item(1,2) = "Last Name" $ws.Cells.Item(1,3) = "EMail Address" $range = $ws.range("A1:C1") $range.font.bold = "true" get-NestedMembers $group
# one-column sort --> works $range1 = $ws.range("A2:C250") $range2 = $ws.range("B2") # Sort by last name [void]$range1.sort($range2, 1,,,,,,,1) $range1.entireColumn.Autofit() #<----------- end script --------------------------------> July 01 How to change the Wins IP address on a computerWere you wondering if anyone has a script that can change the Wins IP address on a server? Get-WmiObject -ComputerName "." ` Win32_NetworkAdapterConfiguration | ?{$_.IPEnabled -eq $True} | ` % {$_.SetWINSServer("172.16.1.240", "172.16.1.241")} I'm guessing you'll want to do this on multiple servers. One way you can do it is read a list of servers from a file into a variable and then run the command on each server: $servers = get-content c:\servers.txt foreach ($server in $servers) { Get-WmiObject -ComputerName $server ` Win32_NetworkAdapterConfiguration | ?{$_.IPEnabled -eq $True} | ` % {$_.SetWINSServer("172.16.1.240", "172.16.1.241")} } June 29 Want to Show a vbScript MsgBox in Powershell ?I wanted to ask before I closed my Excel spreadsheet. In vbScript, I used a msgbox for this.
I found this process for PowerShell and it works.
#<----- Start POSH Script ------------------------------------------------------------->
Function Show-Msgbox {
Param([string]$message=$(Throw "You must specify a message"), [string]$button="okonly", [string]$icon="Question", [string]$title="Message Box" ) # Buttons: OkOnly, OkCancel, AbortRetryIgnore, YesNoCancel, YesNo, RetryCancel # Icons: Critical, Question, Exclamation, Information [reflection.assembly]::loadwithpartialname("microsoft.visualbasic") | Out-Null [microsoft.visualbasic.interaction]::Msgbox($message,"$button,$icon",$title) } $rc=Show-Msgbox -message "Do you want to view the Spredsheet?" `
-icon "exclamation" -button "YesNoCancel" -title "Hey $env:username!!" Switch ($rc) { "Yes" {$x.Visible = $True } # close and release resources "No" {$y.close($false) $xl.quit() spps -n excel } #<---- This will shut down every instance of Excell (for sure!) "cancel" {"When in doubt, punt."} } #<------------ End of Script -------------------------------------------------------------> June 24 Enable the Trusted Platform Module on a Dell OMCI clientMore TPM madness For Dell users only:
Begin vbScript
'**********************************************************************
'*** Name: SampleTrustedPlatformModule.vbs '*** Purpose: To Enable the Trusted Platform Module on a Dell OMCI client. '*** Usage: cscript.exe //nologo SampleTrustedPlatformModule.vbs <systemname> '*** Make sure to open a cmd.com shell as administrator in Vista otherwise UAC will '*** cause it to error out. '*** '*** NOTE: Replace the word yourpassword at the end of line 51 (inside the '*** quotes) with the correct BIOS password if one is set. '*** '*** To only Activate the TPM comment out line 70 “objInstance.Properties_.Item(strPropNameTpmON).Value = 3” '********************************************************************** Option Explicit
'*** Declare variables
Dim strNameSpace Dim strComputerName Dim strClassName Dim strKeyValue Dim objInstance Dim strPropNameTpmON Dim strPropNameTpmActivation Dim strPropValue '*** Check that the right executable was used to run the script
'*** and that all parameters were passed If (LCase(Right(WScript.FullName, 11)) = "wscript.exe" ) Or _ (Wscript.Arguments.Count < 1) Then Call Usage() WScript.Quit End If '*** Initialize variables
strNameSpace = "root/Dellomci" strComputerName = WScript.Arguments(0) strClassName = "Dell_Configuration" strKeyValue = "Configuration" '*** Retrieve the instance of Dell_Configuration class (there should
'*** only be 1 instance). Set objInstance = GetObject("WinMgmts:{impersonationLevel=impersonate,AuthenticationLevel=pktprivacy}//" &_ strComputerName & "/" & strNameSpace & ":" & strClassName & "=" &_ Chr(34) & strKeyValue & Chr(34)) '*** Verify the BIOS Admin Password to enable changes to BIOS settings
'*** Replace ‘password’ in the next line with the BIOS Admin password objInstance.Properties_.Item("Password").Value = "dell123" objInstance.Properties_.Item("PasswordEncrypted").Value = 0 objInstance.Put_ '*** Initialize variables strClassName = "Dell_SMBIOSSettings" strKeyValue = "0" strPropNameTpmON = "TrustedPlatformModule" strPropNameTpmActivation = "TrustedPlatformModuleActivation" '*** Retrieve the instance of Dell_SMBIOSSettings class (there should
'*** only be 1 instance). Set objInstance = GetObject("WinMgmts:{impersonationLevel=impersonate,AuthenticationLevel=pktprivacy}//" &_ strComputerName & "/" & strNameSpace & ":" & strClassName & "=" &_ Chr(34) & strKeyValue & Chr(34)) '*** Set the value of TrustedPlatformModule to '3' ("Enabled")
'*** Set the new value for the property and save the instance objInstance.Properties_.Item(strPropNameTpmON).Value = 3 '*** Set the value of TrustedPlatformModuleActivation to '3' ("Activated")
'*** Set the new value for the property and save the instance objInstance.Properties_.Item(strPropNameTpmActivation).Value = 3 objInstance.Put_ '*** If any errors occurred, let the user know
If Err.Number <> 0 Then WScript.Echo "Enabling Trusted Platform Module failed." End If '*** Sub used to display the correct usage of the script Sub Usage() Dim strMessage strMessage = "incorrect syntax. You should run: " & vbCRLF & _ "cscript.exe /nologo SampleTrustedPlatformModule.vbs <systemname>" WScript.Echo strMessage End Sub June 09 Log Off at end of scriptSo there I was, wondering how to log off from a session after I had finshed my script.
I had to run the the thing under elevated privlages and I was running it from the "run once"
key in the registry, so I wanted it to log off ASAP.
I looked around and poked around and looked in my books, and then I ran across this piece of wisdom from
Set oShell = CreateObject("WScript.Shell")
oShell.Run "logoff", 0, False May 29 Scripting Task Scheduler in VistaI found the need to check several thousand machines on a regular basis to make sure BitLocker was installed and running.
I decided to run a script every time the machine booted up and for that I needed the Task Scheduler.
After some fumbeling around I gat this to work:
'<------------------ Begin Script --------------------------->
Const TASK_ACTION_EXEC = 0
Const TASK_CREATE = 2 Const TASK_Trigger_Boot = 8 Const TASK_RUNLEVEL_HIGHEST = 1 Const TASK_LOGON_PASSWORD = 1 Dim strUser, strPassword
strUser = "domain\svc-account"
strPassword = "Password" Set objService = CreateObject("Schedule.Service") objService.Connect() Set objFolder = objService.GetFolder("\")
Set objTaskDefinition = objService.NewTask(0)
Dim principal
Set principal = objTaskDefinition.Principal ' Set the logon type to TASK_LOGON_PASSWORD
principal.LogonType = 1 ' RUNLEVEL = 1 Tasks will be run with the highest privileges. ' RUNLEVEL = 0 Tasks will be run with the least privileges (LUA). principal.RunLevel = 1
Set colTasks = objTaskDefinition.Triggers
Set objTrigger = colTasks.Create(TASK_Trigger_Boot)
objTrigger.StartBoundary = "2009-05-27T08:00:00-00:00"
objTrigger.ExecutionTimeLimit = "PT5M" 'Five minutes Set colActions = objTaskDefinition.Actions
Set objAction = colActions.Create(TASK_ACTION_EXEC)
objAction.ID = "BitLocker Check"
objAction.Path = "cscript.exe" ' <----- This could be powershell.exe " objAction.WorkingDirectory = "c:\scripts" objAction.Arguments = "/nologo c:\scripts\chkBL.vbs" '<---Put your script here
' (or for PowerShell -noprofile -command "Path to .PS1 script" )
Set objInfo = objTaskDefinition.RegistrationInfo
objInfo.Author = "Author" '<--- Put your name here
objInfo.Description = "Task that checks BitLocker status at every boot." '<--- Put a brief description here.
Set objSettings = objTaskDefinition.Settings
objSettings.Enabled = True objSettings.Hidden = False objSettings.StartWhenAvailable = True objFolder.RegisterTaskDefinition "Check BitLocker Status", objTaskDefinition, TASK_CREATE, strUser, strPassword, 1 '<------------------ End Of Script --------------------------->
Have fun and may the force be with you.
For more info: http://msdn.microsoft.com/en-us/library/aa383607(VS.85).aspx May 21 Enable the Trusted Platform Module (TPM)So in my never ending quest to do obscure stuff, I.E. Script BitLocker I found that I had to make sure the TPM (Trusted Platform Module) was: 1. Enabled 2. Activated 3. Owned (this is handled in another script) Here is the script I came up with to do just that. '< ----- Start vbScript ---------------------------> 'On Error Resume Next Const wbemFlagReturnImmediately = &h10 Const wbemFlagForwardOnly = &h20 Set oSystemSet = GetObject("winmgmts:{impersonationLevel=impersonate," _ & "(Shutdown)}").InstancesOf("Win32_OperatingSystem") arrComputers = Array(".") For Each strComputer In arrComputers WScript.Echo WScript.Echo "==========================================" WScript.Echo "Computer: " & strComputer WScript.Echo "==========================================" TPMOn = False Set objWMIService = GetObject("WinMgmts:{impersonationLevel=impersonate,AuthenticationLevel=pktprivacy}//" _ & "." & "\root\CIMV2\Security\MicrosoftTpm") Set objItems = objWMIService.InstancesOf("Win32_Tpm") For Each objItem in objItems rvaluea = objItem.IsEnabled(A) rvalueb = objItem.IsActivated(B) rvaluec = objItem.IsOwned(C) TPMOn = True WScript.Echo "TPM Is Enabled: " & A WScript.Echo "TPM Is Activated: " & B WScript.Echo "TPM Is Owned: " & C If A AND B AND C Then WScript.Echo "The TPM Is Enabled, Activated and Owned" Else objItem.SetPhysicalPresenceRequest(14) '<---- see note below If Err.Number <> 0 Then WScript.Echo "Enabling Trusted Platform Module failed." End If MsgBox "Will Reboot in 10 seconds" WScript.Sleep 10000 Reboot End If Next If (TPMOn = False) Then WScript.Echo "Trusted Platform Module may be turned off" End If Next Sub Reboot For Each oSystem In oSystemSet oSystem.Win32Shutdown 6 Next End Sub '< ----- end vbScript ---------------------------> I found this information at http://msdn.microsoft.com/en-us/library/aa376478(VS.85).aspx (MSDN) I also had to verify that the TPM was SpecVersion 1.2 or else it won't work with BitLocker.
On Error Resume Next Const wbemFlagReturnImmediately = &h10 arrComputers = Array(".") Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2\Security\MicrosoftTpm") May 19 BitLocker Encryption Progress BarIt can take an hour or two to encrypt your hard drive. I wanted a visual aide to tell me how it was doing.
This vbScript creates a web page and a simple graph that will tell you how far along the encryption process is; On Error Resume Next
Set objExplorer = CreateObject("InternetExplorer.Application")
objExplorer.Navigate "about:blank" objExplorer.ToolBar = 0 objExplorer.StatusBar = 0 objExplorer.Width=200 objExplorer.Height = 450 objExplorer.Left = 0 objExplorer.Top = 0 Do While (objExplorer.Busy)
Wscript.Sleep 200 Loop objExplorer.Document.Title = "BitLocker Conversion Progress" objExplorer.Visible = 1 arrComputers = Array("10", "20", "30", "40", "50", "60", "70", "80", "90", "100")
strHTML = "<h2>BitLocker Conversion Progress</h2>"
strHTML = strHTML & "<table id='ComputerList' width='100%' border>" For Each strComputer in arrComputers
strHTML = strHTML & " <tr>" strHTML = strHTML & " <td width='100%'>" & strComputer & "%</td>" strHTML = strHTML & " </tr>" Next strHTML = strHTML & "</table>"
objExplorer.Document.Body.InnerHTML = strHTML Wscript.Sleep 2000 strComputer = arrComputers(i) Set objWMIService = GetObject("winmgmts:\\" & "." _ & "\root\CIMV2\Security\MicrosoftVolumeEncryption") If Err = 0 Then Set volumes = objWMIService.InstancesOf("Win32_EncryptableVolume") cs = 2 For Each volume in volumes If volume.DriveLetter = "C:" Then retval = volume.GetEncryptionMethod(em) retval1 = volume.GetConversionStatus(cs,ep) If (cs = 1) And (em = 1) Then objExplorer.Quit Wscript.Echo "Ready for EFS decryption!!!!!!!" wscript.quit ' Put decrypt here !!!!!!! ' Loop until Conversion complete ElseIf (cs = 2) Then Do While cs = 2 retval = volume.GetEncryptionMethod(em) retval1 = volume.GetConversionStatus(cs,ep) Wscript.Echo em & vbTab & cs & vbTab & ep If ep >=10 Then i = 0 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep >=1 And ep < 10 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep > 20 Then i = 1 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep > 10 And ep < 20 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep > 30 Then i = 2 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep > 20 And ep < 30 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep > 40 Then i = 3 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep > 30 And ep < 40 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep > 50 Then i = 4 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep >= 40 And ep < 50 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep > 60 Then i = 5 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep >= 50 And ep < 60 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep > 70 Then i = 6 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep >= 60 And ep < 70 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep > 80 Then i = 7 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep >= 70 And ep < 80 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep > 90 Then i = 8 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep >= 80 And ep < 90 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If If ep = 100 Then i = 9 objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "lightgreen" ElseIf ep >= 90 And ep < 100 Then objExplorer.Document.Body.All.ComputerList.Rows(i).bgColor = "yellow" End If i = 0 Wscript.Sleep 60000 Loop End If End If Err.Clear Next End If May 06 Scripting BitLockerFor those of you who read this (that would be me and my sainted mother) I have been a bit remiss in updating this blog. I have been between opportunities, as they say. As luck would have it, I found a contract that requires me to script a BitLocker implementation. The first issue I had to deal with was determining if BitLocker was already installed. That proved to be no easy task for me. With some help and luck I came up with this; dim retval, em
arrComputers = Array(".") For Each strComputer In arrComputers WScript.Echo WScript.Echo "==========================================" WScript.Echo "Computer: " & strComputer WScript.Echo "==========================================" Set objWMIService = GetObject("winmgmts:\\" & strComputer _ Set volumes = objWMIService.InstancesOf("Win32_EncryptableVolume") & "\root\CIMV2\Security\MicrosoftVolumeEncryption") for each volume In volumes
If volume.DriveLetter = "C:" then retval = volume.GetEncryptionMethod(em) WScript.Echo em End If Next Next If it returns 1 (one) BitLocker is enabled and running. 0 (zero) means it is not.
And here it is in Powershell;
$strComputer = "."
$colItems = Get-WmiObject -class Win32_EncryptableVolume -namespace "root\CIMV2\Security\MicrosoftVolumeEncryption" `
-computername $strComputer -filter "DriveLetter='C:'" $b = $colItems.GetEncryptionMethod()
write-host "EncryptionMethod: " $b.EncryptionMethod You can also check on the status of your Conversion by adding a line;
retval1 = volume.GetConversonStatus(cs, ep) to the vbScript.
Like this:
dim retval, em
arrComputers = Array(".") For Each strComputer In arrComputers WScript.Echo WScript.Echo "==========================================" WScript.Echo "Computer: " & strComputer WScript.Echo "==========================================" Set objWMIService = GetObject("winmgmts:\\" & strComputer _
& "\root\CIMV2\Security\MicrosoftVolumeEncryption") Set volumes = objWMIService.InstancesOf("Win32_EncryptableVolume") for Each volume in volumes
If volume.DriveLetter = "C:" then retval = volume.GetEncryptionMethod(em) retval1 = volume.GetConversionStatus(cs, ep) ' <--- added line Wscript.Echo "Conversion Status: " & cs & vbTab & "% Complete: " & ep End If Next Next And in PowerShell;
$strComputer = "."
$colItems = Get-WmiObject -class Win32_EncryptableVolume -namespace "root\CIMV2\Security\MicrosoftVolumeEncryption" `
-computername $strComputer -filter "DriveLetter='C:'" $c = $colItems.GetConversionStatus()
$ep = $c.EncryptionPercentage $ep if ($C.ConversionStatus -eq 0)
{"Conversion Status: FULLY DECRYPTED"} elseif($C.ConversionStatus -eq 1) {"Conversion Status: FULLY ENCRYPTED"} elseif($C.ConversionStatus -eq 2) {"Conversion Status: ENCRYPTION IN PROGRESS"} elseif($C.ConversionStatus -eq 3) {"Conversion Status: DECRYPTION IN PROGRESS"} elseif($C.ConversionStatus -eq 4) {"Conversion Status: ENCRYPTION PAUSED"} elseif($C.ConversionStatus -eq 5) {"Conversion Status: DECRYPTION PAUSED"} else {$C.ConversionStatus + " Conversion Status: unknown"} And here are some words from Microsoft on the subject:
Although the documentation was in C language notation, it is similar in call in VBScript. According to the documentation, it is: uint32 GetEncryptionMethod([out] uint32 EncryptionMethod); For more information, see: GetEncryptionMethod Method of the Win32_EncryptableVolume Class http://msdn.microsoft.com/en-us/library/aa376434(VS.85).aspx
You will need to also modify the calls for the GetProtectionStatus and GetConversionStatus calls, otherwise, this too will only output their respective Return Value. Below is the syntax for the 2 other methods that you are calling.
In VBScript, they would look like:
returnValue1 = GetProtectionStatus(ps) WScript.Echo "ProtectionStatus: " & ps returnValue2 = GetConversionStatus(cs, ep) WScript.Echo "ConversionStatus: " & cs WScript.Echo "EncryptionPercentage: " & ep For more information, see: GetProtectionStatus Method of the Win32_EncryptableVolume Class http://msdn.microsoft.com/en-us/library/aa376448(VS.85).aspx GetConversionStatus Method of the Win32_EncryptableVolume Class http://msdn.microsoft.com/en-us/library/aa376433(VS.85).aspx March 12 Check if a Process is running. If not start it and send an Email# COMMENT: Check if Process is running. If not start it and send an Email
# # =======PowerShell Script======================================== if (!(gps myproc -ea 0)) { $MyProcCMD = "c:\program file\my program\myproc.exe /switces" #<-- cmd to start proc $wmi = ([wmiclass]"win32_process").Create($MyProcCMD) if ($wmi.returnvalue -eq 0) { $smtp = new-object system.net.mail.smtpClient("smtp.domain.com") $mail = new-object System.Net.Mail.MailMessage $mail.from = "f...@domain.com" $mail.to.add("t...@domain.com") $mail.subject = "MyProc was in Stopped state. Successfully restarted." $mail.body = "bla-bla-bla" $mail.IsBodyHtml = $true $smtp.send($mail) } Remote Software InventorySo you want to check a server named firefly and see what's installed on it and the version
This script querys the remote computer's uninstall keys and writes out the Display Name and Display version
of everything it finds.
#<----Start PowerShell Script ------->
$computer = "firefly"
$uninstallkey="Software\Microsoft\Windows\CurrentVersion\Uninstall" $keytype=[Microsoft.Win32.RegistryHive]::LocalMachine $remotebase=[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($keytype,$computer) $regkey=$remotebase.OpenSubKey($uninstallkey) $regkey.GetSubKeyNames() | foreach {
$DisName = $remotebase.OpenSubKey("$uninstallkey\$_").GetValue("DisplayName") $DisVer = $remotebase.OpenSubKey("$uninstallkey\$_").GetValue("DisplayVersion") Write-Host $DisName "`t" $DisVer } #<---- End Script --------->
Looking for something more specific? Like Adobe Flash?
#<----Start Script ------->
$computer = "firefly" #<---- End Script ---------> February 27 Search for file type on a list of ComputersThis is one of the very first things I tried to do with PowerShell. It searches a list of computers
for Access Data bases and prints the ones it finds to an Excel spreadsheet.
#<--- Start Script --------->
$a = Get-Date -format g
$row = 2 $xl = New-Object -c excel.application $xl.visible = $True $wb = $xl.workbooks.add() $sh = $wb.sheets.item(1) $sh.Range("A1:T1").Font.Bold = $true $sh.Range("A:A").Font.Bold = $True $sh.Cells.Item(1, 1) = "Computer Name" $sh.Cells.Item(1, 2) = "Date Run" + $a $sh.Cells.Item(1, 3) = "FullName" $sh.Cells.Item(1, 4) = "Length" $sh.Cells.Item(1, 5) = "Owner" $sh.Cells.Item(1, 6) = "Extension" $sh.Cells.Item(1, 7) = "LastAccessTime" $sh.Cells.Item(1, 8) = "CreationTime" $sh.Cells.Item(1, 9) = "Group Access" # you will need a list of computers, one computer name per line
$computerlist = Get-Content 'c:\scripts\Servers.txt' foreach ($srv in $computerlist) { $sh.Cells.Item($row, 1) = $srv $response = Get-WmiObject -query "Select * From Win32_PingStatus Where Address = '$srv'" if( ($response -eq $null) -or ($response.StatusCode -ne 0)) { $sh.Cells.Item($row, 2).Font.ColorIndex = 3 $sh.Cells.Item($row, 2) = "Does Not Ping" $row++ } else { if ($response.TimeToLive -le 64) { $sh.Cells.Item($row, 2).Font.ColorIndex = 5 $sh.Cells.Item($row, 2) = "probably is a Unix host" $row++ } Else { # Change the path and the extension. If you want all files, remove the -include *.??? # This might be faster get-childitem -path \\$srv\c$ -filter "*.mdb" -r -ea continue | Get-ChildItem -path \\$srv\c$ -recurse -include *.mdb -ea continue |
foreach { $file = New-Object -TypeName System.Management.Automation.PSObject Add-Member -InputObject $file -MemberType NoteProperty -Name "FullName" -Value $_.Fullname Add-Member -InputObject $file -MemberType NoteProperty -Name "LastAccessTime" -Value $_.LastAccessTime Add-Member -InputObject $file -MemberType NoteProperty -Name "LastWriteTime" -Value $_.LastWriteTime $acl = Get-Acl -Path $_.FullName Add-Member -InputObject $file -MemberType NoteProperty -Name "Owner" -Value $acl.Owner Add-Member -InputObject $file -MemberType NoteProperty -Name "AccessToString" -Value $acl.AccessToString Add-Member -InputObject $file -MemberType NoteProperty -Name "Group" -Value $acl.Group $sh.Cells.Item($row, 3) = $_.FullName $sh.Cells.Item($row, 4) = $_.Length $sh.Cells.Item($row, 5) = $acl.Owner $sh.Cells.Item($row, 6) = $_.Extension $sh.Cells.Item($row, 7) = $_.LastAccessTime $sh.Cells.Item($row, 8) = $_.CreationTime $sh.Cells.Item($row, 9) = $acl.Group $sh.Cells.EntireColumn.AutoFit() $row++ } } } } $wb.SaveAs("C:\temp\find_files.xls")
# close and release resources $wb.close($false) $xl.quit() spps -n excel #---- End Script --------------> |
|
|