<

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x

PowerShell dynamic web interface

Published on
13,813 Points
10,713 Views
1 Endorsement
Last Modified:
This is a PowerShell web interface I use to manage some task as a network administrator. Clicking an action button on the left frame will display a form in the middle frame to input some data in textboxes, process this data in PowerShell and display some results in bottom frame.
PowerShell web interface

Goal:
A network administrator has many tasks that can be done manually or with scripting:
  • Creating users in Active Directory
  • Listing all Active Directory groups for management
  • Creating Office 365 sites and libraries
Solution:
At first, I did this interface in VBS/WSH, but neither Office 365 nor SharePoint can be accessed by VBS/WSH, so I made this version in PowerShell to be able to perform more .net related network tasks.

Operation:
This uses the Internet Explorer 10 and 11 com object, and was tested in Windows 7 Pro Service Pack 1. You will have to change the signing policy of PowerShell to use this script as it is not signed. The command to do this in the ISE PowerShell interface is: Set-ExecutionPolicy RemoteSigned. It will set the security level of PowerShell so that only remote script need signing, not local ones.

1. The PowerShell script uses an array that contains the name and attributes of the control buttons that the interface will display in the left frame. I called it a dynamic web interface, because the PowerShell script will generate dynamically the web interface for you with the buttons you need in this array:
 
[string[]]$arabutnam="test"
[string[]]$arabutdes="test something"
[string[]]$aradepnam="Change"
[string[]]$aradepcol="cccccc"

Open in new window


You can change the button names in the array. You can add more buttons for more task to manage.

2. The main loop watches the button's state (HTML elements value) in a loop to get a string containing the button name. When a button is clicked, its name will be stored in the string variable: $resbutstr

When you press "test", the main loop will execute some code, because the $resbutstr value is equal to the button name "test":
 
if ($resbutstr -eq "test")
{
# some stuff is executed here when test is pressed
}

Open in new window


3. Also included in this script is a function to generate an HTML form. So when we press "test", another array that contain the form input boxes to present to the user is processed and an HTML form with textboxes is created to get some value from the user:
 
[string[]]$parprefix="Name of the project to create"
[string[]]$parname="name01"
[string[]]$pardefault="helloworld"
[string[]]$partype="textbox"
[string[]]$parsuffix="Entrez un numéro de projet"

# parameter number 2 to inputbox
$parprefix+="stuff to enter"
$parname+="name02"
$pardefault+="salutlesamis"
$partype+="textbox"
$parsuffix+="ceci est un test"

[string[]]$parbuttons="ok"
$parbuttons+="cancel"

$dynamicformparamout01 = dynamicform($parprefix, $parname, $pardefault, $partype, $parbuttons, $parsuffix,

Open in new window


4. After the dynamicform function has completed the input, the values are stored in an array of string. We can use the values we obtained in the array as parameters to process more things in PowerShell. The results from the HTML input boxes are in this array:
 
foreach ($element in $dynamicformparamout01.araresults) {}

Open in new window


In this model, we just display the results in the array, in an HTML table in the bottom frame

5. If another button on the left is pressed when we are at the step of entering data, it will exit this loop and return to main loop, then do the action of the button that was pressed before resetting its value in the left HTML frame to 0.

I will personally use the interface to create a website for each new project in Office 365 with three libraries, and a nice standard menu.
########################## file.ps1 ##############################
#2016-04-03
#added some innerhtml to clear frames properly

#2016-04-02
#First draft of the powershell version of my web interface for network administrators

#Basically, it is:
#A powershell script to use internet explorer as interface for more powershell scripts
#It start internet explorer, divide it in 3 frames (flef, fmid and fbot)
#You must run "Set-ExecutionPolicy RemoteSigned" because this script is not signed
#(this will allow unsigned local powershell scripts to run)
#In the left frame, there is control buttons
#In middle frame (up) you can input some data to be processed
#(it's just some textbox in html that return their results to powershell)
#In the bottom frame, you can display the processing or the results of your scripts

########################################################
# requirement: Set-ExecutionPolicy RemoteSigned
########################################################

# powershell managed web interface for a network administrator

# par: serge.fournier(a)hotmail.com
# 2016-03-28

# variables typed

add-type @"
public struct oieparamin01
{
public int height01;
public int width01;
public string[] arabutnam;
}
"@

add-type @"
public struct oieparamout01
{
public object oie;
public object flef;
public object fmid;
public object fbot;
}
"@

add-type @"
public struct dynamicformparamout
{
public string[] arabutnam;
public string[] araresults;
}
"@



###########################################
# wait for com object OIE to be ready
###########################################
function isoieready($oie)
{
$millisec = 50
While (($oie.ReadyState -ne 4) -and ($oie.hwnd -ne $null)) { Start-Sleep -Milliseconds $millisec}
While (($oie.busy) -and ($oie.hwnd -ne $null)) { Start-Sleep -Milliseconds $millisec}
return $oie.hwnd
}

##########################################
# internet explorer
##########################################
function makenewOIE($oieparamin01)
{

$oieparamout01 = new-object oieparamout01;

# start ie object
$oie = new-object -com InternetExplorer.Application
$oie.FullScreen = $False

# wait for a time then fatal error if iexplore does not get created
$timeout01 = 0
#$ieStatus01 = Get-ProcessWithOwner iexplore

$oIE.left=0 # window position
$oIE.top = 0 # and other properties
$oIE.height = $oieparamin01.height01
$oIE.width = $oieparamin01.width01
$oIE.menubar = 1 #=== no menu
$oIE.toolbar = 1
$oIE.statusbar = 1
$oIE.RegisterAsDropTarget = $True
$oie.Navigate("about:blank")
$oie.document.title = "Skynet interface"

# calculate for ie to take all screen but a small lane of 10% up top
# get screen resolution
[void] [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$Screens = [system.windows.forms.screen]::AllScreens

$DeviceName = $Screens[0].DeviceName
$oiewid = $Screens[0].Bounds.Width
$oiehei = $Screens[0].Bounds.Height
$IsPrimary = $Screens[0].Primary

$sizwidpercent = 100
$sizheipercent = 95
$loswid = 100-$sizwidpercent
$loshei = 100-$sizheipercent
$newwid = $oiewid*$sizwidpercent*.01
$newhei = $oiehei*$sizheipercent*.01
# this stuff work in ISE console but not outside of it
try {$oie.document.parentwindow.resizeto($newwid,$newhei)} catch {}
$newx = $oiewid * $loswid * .01 /2
$newy = $oiehei * ($loshei/2) * .01 /2
try {$oie.document.parentwindow.moveto($newx, $newy)} catch {}
$oie.addressbar=$false

$oie.visible = $true

$dummy = isoieready($oie)

$doctit = "SkynetPS"

$h = "&lt;HTML&gt;"
$h+="<HEAD><TITLE>" + $doctit + "</TITLE>"
$h+="<meta content=""text/html; charset=utf-8"" http-equiv=""Content-Type"">"
$h+="<meta http-equiv=""X-UA-Compatible"" content=""IE=8"">"
$h+="</HEAD>"
$h+="<FRAMESET id='main' COLS=""13%, *"">"
$h+="<FRAME SRC=""About:Blank"" NAME=""left"" id=""left"">"
$h+="<frameset id='main2' rows=""30%,70%"">"
$h+="<FRAME SRC=""About:Blank"" NAME=""middle"" id=""middle"">"
$h+="<FRAME SRC=""About:Blank"" NAME=""bottom"" id=""bottom"">"
$h+="</FRAMESET>"
$h+="</frameset>"
$h+="</HTML>"

$oIE.document.documentelement.innerhtml = $h

$dummy = isoieready($oie)

# get ie version by register base
# https://gallery.technet.microsoft.com/scriptcenter/Servers-Inventory-report-97da5709

# get ie file version, 9 and before does not act like 10 and after for frame objects
$fileversion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\program files\internet explorer\iexplore.exe").FileVersion
$finddot = $fileversion.indexof(".")
$fileversion2 = $fileversion.substring(0,$finddot)

if (($fileversion2 -eq "10") -or ($fileversion2 -eq "11") -or ($fileversion2 -eq "12"))
{
#$flef = $oie.parent.document.getElementByid("left").document.documentelement #for innerhtml, not working in frame...
$flef = $oie.parent.document.getElementByid("left").contentdocument
$fmid = $oie.parent.document.getElementByid("middle").contentdocument
$fbot = $oie.parent.document.getElementByid("bottom").contentdocument
}
else
{
$flef = $oie.document.frames("left").document
$fmid = $oie.document.frames("middle").document
$fbot = $oie.document.frames("bottom").document
}

### buttons to display in web interface



#$fmid.IHTMLDocument2_write("arabutnam of 0: " + $arabutnam[0])

$h = "<html><body>"
#$h+="<body background=""" & basedir & "LOGO-STAS-FOND.jpg"">")
$h+="<h3><span class=SpellE>" + $maitit + "</span></h3>"
$h+="<form name='form1'>"

if (isoieready($oie)) {$flef.IHTMLDocument2_write($h)}

$i = 0
foreach ($element in $arabutnam)
{
#$fmid.IHTMLDocument2_write($element)

$h = "<input type=""hidden"" name=""" + $arabutnam[$i] + """ value=""0"">"
$h+= "<input type=""button"" height:50px;font-size:14px;width:100%;"" value=""" + $arabutdes[$i] + """ " #name=""" + $arabutnam[$i] + """
$h+= "onclick=""" + $arabutnam[$i] + ".value=1"""

$i2=$i
if ($i -gt $arabutnam.getupperbound(0))
{
$i2=$arabutnam.getupperbound(0)
}
else
{
$a=$aradepnam[$i2]
}
if ($a -ne $lasdep)
{
#=== new departement name
if (isoieready($oie)) {$flef.IHTMLDocument2_write("<br>" + $a + "<br>")}
$lasdep=$a
}
$h+=" background-color: #" + $aradepcol[$i2] + "; color: #000000;""><br>"
if (isoieready($oie)) {$flef.IHTMLDocument2_write($h)}
$i=$i+1
}

$h = "</form>" + "</body>" + "</html>"
$flef.IHTMLDocument2_write($h)

$oieparamout01.oie = $oie
$oieparamout01.flef = $flef
$oieparamout01.fmid = $fmid
$oieparamout01.fbot = $fbot

return $oieparamout01

}
################################################
# dynamicform in html
################################################

function dynamicform()
{
#dynamicform($pardescription, $parname, $pardefault, $partype, $parbuttons, $parvalidation, $parsuffix)
if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " dynamic form generation" | Out-File $filnam01 -append}

$dynamicformparamout01 = new-object dynamicformparamout;
$resbutstr = ""
$resbut = 0
$validation = 0
$result= $null

do
{
if ($validation -eq 0)
{
$h= "<body>"
$h+= "<html>"

# focus on a textbox (usually the first one)
$h+= "<BODY onLoad=""document.form01." + $parname[0] + ".focus()"">"

$h+= "<div class=MsoNormal align=center style='text-align:center'>"
$h+= "</div>"
$h+= "<form name=form01>"

# input fields/textbox
$i = 0
foreach ($element in $parname)
{
$h+= $parprefix[$i] + ": "
$h+= "<input type=" + $partype[$i] + ""
$h+= " id=" + $parname[$i]
$h+= " NAME=""" + $parname[$i] + """ size=""20"" value=""" + $pardefault[$i] + """ onKeypress=""return event.keyCode!=13"""

$col = "blue"

$h+= "&nbsp; <b><span style='color:" + $col + "'>&nbsp" + $parsuffix[$i] + "</span></b><br style='mso-special-character:line-break'>"

$h+= "<![if !supportLineBreakNewLine]><style='mso-special-character:line-break'>"
$h+= "<![endif]></p>"
$i++
}

# button ok and cancel
$i = 0
foreach ($element in $parbuttons)
{
$typ01 = "button"
$h+= "<input type=""hidden"" name=""" + $element + """ value=""0"">"
$h+= "<input type=""" + $typ01 + """ value=""&nbsp;" + $element + "&nbsp;"" "
$h+= "onclick=""" + $element + ".value=1"">"
#$h+= "<input type=""" + $typ01 + """ name=""" + $element + """ value=""&nbsp;" + $element + "&nbsp;"">"
$h+= "&nbsp&nbsp&nbsp"
$i++
#$h = "<input type=""hidden"" name=""" + $arabutnam[$i] + """ value=""0"">"
#$h+= "<input type=""button"" height:50px;font-size:14px;width:100%;"" value=""" + $arabutdes[$i] + """ " #name=""" + $arabutnam[$i] + """
#$h+= "onclick=""" + $arabutnam[$i] + ".value=1"""
}

$h+= "</div>"
$h+= "</form>"
$h+= "</body>"
$h+= "</html>"
#IF (isoieready($oie)) {$fmid.IHTMLDocument2_write($h)}

IF (isoieready($oie)) {$fmid.body.innerhtml = $h}
}
else
{
# dont display stuff again, wait for ok to click

}
start-sleep -m 200

# button dynamic form check
foreach ($element in $parbuttons)
{
if ($resbutstr -eq "")
{
if (isoieready($oie)) {$resbut = $fmid.getElementByID($element).value}
if ($resbut -ne 0) {$resbutstr = $element}
}
}

# buttons left frame check
foreach ($element in $arabutnam)
{
if ($resbutstr -eq "")
{
if (isoieready($oie)) {$resbut = $flef.getElementByID($element).value}
if ($resbut -ne 0) {$resbutstr = $element}
}
}

if ($resbutstr -ne "")
{
if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + $resbutstr + " was pressed" | Out-File $filnam01 -append}

# validate result and reloop if something is wrong



}

# will return result after sub
$dynamicformparamout01.arabutnam = $resbutstr.ToLower()
$resbutstr = $resbutstr.ToLower()
if ($resbutstr -eq "ok")
{
# validate results


$i = 0
$result = $null
foreach ($element in $parname)
{
$resultdummy = ""
if (isoieready($oie)) {$resultdummy = $fmid.getElementByID($parname[$i]).value}
[string[]]$result+= $resultdummy

#if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " stuff: " + $i + " " + $result[$i]| Out-File $filnam01 -append}
$i++
}

}
else
{
# something else than OK was pressed
$result = $null
}

$validation=1
} until ((($resbutstr -ne "") -and ($validation -eq 1)) -or ($oie.hwnd -eq $null))

# reset ok and cancel buttons value to 0
foreach ($element in $parbuttons)
{
if (isoieready($oie)) {$fmid.getElementByID($element).value=0}
}

# reset left frame buttons value to 0
foreach ($element in $arabutnam)
{
if (isoieready($oie)) {$flef.getElementByID($element).value=0}
}
$dynamicformparamout01.araresults = $result
return $dynamicformparamout01
#, $resbutcancel
}


##############################################
# START
##############################################

# get powershell version
#$PSVersionTable

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$Logfilename ="zzz_skynet_log.txt"
$filnam01 = $scriptPath + "\" + $Logfilename
$logall = 1
$dynamicformparamout01 = new-object dynamicformparamout;

if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " START" | Out-File $filnam01}

$maitit = "SkynetPS 1.0"



$project01 = "test"
#$project01 = "office365"

# each project have it's own buttons in left frame

if ($project01 -eq "test")
{
# buttons to display in internet explorer left frame
[string[]]$arabutnam="test"
[string[]]$arabutdes="test something"
[string[]]$aradepnam="Change"
[string[]]$aradepcol="cccccc"
}

if ($project01 -eq "office365")
{
# buttons to display in internet explorer left frame
[string[]]$arabutnam="createlibrary"
[string[]]$arabutdes="office 365 library creation"
[string[]]$aradepnam="Change"
[string[]]$aradepcol="cccccc"
}


# buttons for all projects

$arabutnam+="info01"
$arabutdes+="Information"
$aradepnam+="all"
$aradepcol+="cccccc"

$arabutnam+="quit01"
$arabutdes+="Quitter"
$aradepnam+="all"
$aradepcol+="cccccc"

# internet explorer interface for powershell
$oieparamin01 = new-object oieparamin01;
$oieparamin01.height01 = 900
$oieparamin01.width01 = 1800
# we call the fonction with a typed/struct variable as parameter, this way we can change the number of parameters dynamically
$oieparamout01 = new-object oieparamout01;
$oieparamout01 = makenewOIE($oieparamin01)
# put the frames in friendly variables names
$oie = $oieparamout01.oie
$flef = $oieparamout01.flef
$fmid = $oieparamout01.fmid
$fbot = $oieparamout01.fbot

#=== make internet explorer the main active window
#a = objShe.AppActivate("http:/// - " & doctit & " - M")
#a = objShe.AppActivate(doctit & " - M")

$usenam = [Environment]::UserName

#$fmid.IHTMLDocument2_write([System.Security.Principal.WindowsIdentity]::GetCurrent().Name + "<br>")

#=== string result for a button press in a frame (lef = left frame, mid = middle frame (up))
$resbutstr=""

$reskeylef=0
$reskeymid=0
$reskeybot=0

#=== we also chek the key presse in each frame
#=== we do this cause we want "enter" key to be used instead of pressing "ok" button with the mouse
#$flef.onkeypress = GetRef("Checklef")
#$fmid.onkeypress = GetRef("Checkmid")
#$fbot.onkeypress = GetRef("Checkbot")

#############################################
# main loop
#############################################

While (($resbutstr –ne "quit01") -and ($oie.hwnd -ne $null))
{
start-sleep -m 200

# buttons left frame check
# lefbutstr will contain button name

foreach ($element in $arabutnam)
{
if ($resbutstr -eq "")
{
if (isoieready($oie)) {$resbut = $flef.getElementByID($element).value}
if ($resbut -ne 0) {$resbutstr = $element}
}
}
# this was just the value contained in the html invisible button paired with the real button
# since we have button name, we do not need it anymore
$resbut = 0
$resbutstr = $resbutstr.tolower()
if ($resbutstr -eq "test")
{
# before we go to the dynamic form, we reset left frame button value
if (isoieready($oie)) {$flef.getElementByID($resbutstr).value = 0}

# TEST reset button value (unpress the button)

if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " button TEST was clicked" | Out-File $filnam01 -append}

# clear frame
if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " clearing frame" | Out-File $filnam01 -append}
IF (isoieready($oie)) {$fmid.body.innerhtml = ""}
if (isoieready($oie)) {$fbot.body.innerhtml = ""}

# get and validate some parameters

[string[]]$parprefix="Name of the project to create"
[string[]]$parname="name01"
[string[]]$pardefault="helloworld"
[string[]]$partype="textbox"
[string[]]$parsuffix="Entrez un numéro de projet"

# parameter number 2 to inputbox
$parprefix+="stuff to enter"
$parname+="name02"
$pardefault+="salutlesamis"
$partype+="textbox"
$parsuffix+="ceci est un test"

[string[]]$parbuttons="ok"
$parbuttons+="cancel"

$dynamicformparamout01 = dynamicform($parprefix, $parname, $pardefault, $partype, $parbuttons, $parsuffix, $arabutnam)

# all value returned by the web form are in $result array

$i=0

if ($dynamicformparamout01.arabutnam -eq "ok")
{
##############################################
# do some stuff
# 1 create folder structure
# 2 create a ldap user
# 3 create ldap group
# 4 create a office 365 site and library
# 5 extract documents from office 365 based on terms and major version
##############################################

# clear frame
IF (isoieready($oie)) {$fmid.body.innerhtml = ""}
$h = ""
$i = 0

$h+= "<table width=""100%"" BORDERCOLOR=""black"" class=MsoTableGrid border=1 CELLSPACING=0 cellpadding=2 style='border-collapse:collapse;border: 1px solid black'>"

foreach ($element in $dynamicformparamout01.araresults)
{
$h+= "<tr>"
# display results in fbot frame
$h += "<td>" + $parprefix[$i] + "</td>"
$h += "<td>" + $dynamicformparamout01.araresults[$i] + "</td>"
if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " stuff2: " + $i + " " + $dynamicformparamout01.araresults[$i]| Out-File $filnam01 -append}
$h+= "</tr>"
$i++
}

$h+="</table>"
IF (isoieready($oie)) {$fbot.body.innerhtml = $h}

# reset button value for left frame (unclick the button)
if (isoieready($oie)) {$flef.getElementByID($resbutstr).value = 0}
$resbutstr = ""
$resbut = 0

}
elseif ($dynamicformparamout01.arabutnam -eq "cancel")
{
if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " another button was pressed: " + $dynamicformparamout01.arabutnam| Out-File $filnam01 -append}

# reset button value for left frame (unclick the button)
if (isoieready($oie)) {$flef.getElementByID($resbutstr).value = 0}
$resbutstr = ""
$resbut = 0
if (isoieready($oie)) {$fbot.body.innerhtml = ""}
if (isoieready($oie)) {$fmid.body.innerhtml = ""}
}
else
{
if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " another button was pressed: " + $dynamicformparamout01.arabutnam| Out-File $filnam01 -append}
# since another button on left frame was pressed
# no action is done here, and the button stay pressed until it's action is done
# clear all frames
if (isoieready($oie)) {$fbot.body.innerhtml = ""}
if (isoieready($oie)) {$fmid.body.innerhtml = ""}
$resbutstr = $dynamicformparamout01.arabutnam

}


}


if ($resbutstr -eq "info01")
{
if (isoieready($oie)) {$flef.getElementByID($resbutstr).value = 0}
if (isoieready($oie)) {$fbot.body.innerhtml = ""}
if (isoieready($oie)) {$fmid.body.innerhtml = ""}

# main action for this button
$h = "<br>Powershell web interface<br>"
$h+= "<br>This program was made by: sergefournier(@)hotmail.com<br>"
$h+= "<br>2016-03-04 added some innerhtml to clear frames<br>"
if (isoieready($oie)) {$fbot.body.innerhtml = $h}

$resbutstr = ""
$resbut = 0

}

# system string does not have tolower method so it generate an error
try {$resbutstr = $resbutstr.tolower()} catch {}

}

$dummy = isoieready($oie)

if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " oie state: " + $dummy | Out-File $filnam01 -append}

if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " button pressed: " + $resbutstr | Out-File $filnam01 -append}

if ($logall=1) {(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " END quit was pressed or internet explorer closed" | Out-File $filnam01 -append}

# end of program, quit was pressed or OIE handle is null
if (isoieready($oie))
{
$fmid.IHTMLDocument2_write("EXIT " + $resbutlefstr + "<br>")
$oie.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($oie)
}


#Remove-Variable $oie

Open in new window

 
1
1 Comment
LVL 12

Author Comment

by:Serge Fournier
Bug in windows 10
I had to add ID tags on the invisibles buttons. The name was not used as ID in windows 10.
Line 184 (for lefct frame)
        $h = "<input type=""hidden"" id=""" + $arabutnam[$i] + """ name=""" + $arabutnam[$i] + """ value=""0"">"
Line 271
$h+= "<input type=""hidden"" id=""" + $element + """ name=""" + $element + """ value=""0"">"
1

Featured Post

OWASP: Threats Fundamentals

Learn the top ten threats that are present in modern web-application development and how to protect your business from them.

The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.
Other articles by this author

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month