Powershell code errors when executed using 'Run with PowerShell' but runs OK in PS Editor - powershell

Work continues on migrating my HTA to an XAML-based, PowerShell-driven form...
When I run my script inside PSE, the hiding/unhiding of grids using the 'Visibility' property, driven from the user selecting a drop-down item, works great. However, if I run it using Explorer's 'Run with PowerShell' context menu, or from a PS command prompt, I get the error "The property 'Visibility' cannot be found on this object" when the drop-down selection changes.
For completeness, here's the function which is in an imported .PSM1 file:
Function cmbDeploymentPurpose_OnSelectionChanged($Form, $SelectedValue){
If ($SelectedValue.ToString() -eq "System.Windows.Controls.ComboBoxItem: Available")
{
$grdDeploymentSettings_Available.Visibility = "Visible"
$grdDeploymentSettings_Required.Visibility = "Hidden"
$grdScheduling_Required.Visibility = "Hidden"
}
ElseIf ($SelectedValue.ToString() -eq "System.Windows.Controls.ComboBoxItem: Required")
{
$grdDeploymentSettings_Available.Visibility = "Hidden"
$grdDeploymentSettings_Required.Visibility = "Visible"
$grdScheduling_Required.Visibility = "Visible"
}
}
and here's the relevant event creation:
$cmbDeploymentPurpose.Add_SelectionChanged({cmbDeploymentPurpose_OnSelectionChanged $Form $cmbDeploymentPurpose.SelectedValue}.GetNewClosure())
In each case, the user credentials are the same.

Related

PowerShell: How to clear cache of included files?

I include an external .ps1 into antother .ps1:
foo.ps1:
.("C:\test\bar.ps1");
$obj = [bar]::new();
$obj.out();
bar.ps1:
class bar{
$output;
bar(){
$this.output = 1;
}
[void] out(){
write-host $this.output;
}
}
The first time I execute foo.ps1 in the Windows PowerShell ISE the output is "1", as expected.
Then I go to bar.ps1 and change $this.output = 1; to $this.output = 2;. After executing foo.ps1 again the output is still "1". When I change something in foo.ps1, like simply appending a new line, and execute it once again, the output becomes "2". Changing back, like removing the new line, will make an output of "1" again.
For me it looks like an caching issue. Is it possible to clear or disable the caching?
Thanks in advance!

Return output from Powershell script to UIPath using Invoke power shell

I am trying to get a value from an input box from a Powershell script back to a UI Path Sequence. I have created a simple sequence as an example. Yes, I know I could do this all in UI Path, I am just using an easy example as a way to try and test this for future use cases. Here is my sequence:
My text file from "Read text file" is:
$test = "Desktop/PassingArgs2of2.ps1 -Message foo"
Invoke-Expression -Command $test
The activity in UiPath looks like so:
The psCmd that I am running in the Invoke power shell activity looks like this:
Param(
[parameter(Mandatory=$true)]
[string]
$Message)
try{
$Global:fooVar = $null
function Test-InputBox(){
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$msg = "fooMsg"
$title = "fooTitle"
$localtest = [Microsoft.VisualBasic.Interaction]::InputBox($msg, $title)
$Global:fooVar = $localtest.ToString()
}
Test-InputBox
}
catch{}
I tried setting fooVar equal to testLocal in the PowerShellVariables within Invoke power shell and then writing it, but that did not work.
Basically I want to get fooVar back into UI Path. Any help would be greatly appreciated. Thank you!
You're almost there. First, your Powershell script has to return a value. Take this for example:
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$title = 'Your title goes here'
$msg = 'Your favorite color:'
$text = [Microsoft.VisualBasic.Interaction]::InputBox($msg, $title)
return $text
Here's the script in action (note that I called it twice and provided "red" the first time:
Then, just use this script directly in the Invoke Powershell activity. Note that the most important part here is the Output property - here, I decided to go for an array of strings. Naturally, as we only return a single value, you can just access the text provided by the user by accessing output(0).ToString().

Setting StrongAuthenticationUserDetails PhoneNumber for AzureAD via Powershell?

That title really flows.
When setting up computers for use with Azure Active Directory, we would have IT do initial setup and config. This included the first sign in and joining to Azure Active Directory. When signing in it forces you to select a verification method. We would use our desk phone or cell phone for ease.
The time has come for us to update that second factor phone number. I know of a way to manually do it via the Azure AD Web UI, but I am looking for a scripted way to set that number in PowerShell.
Here is how I retrieve the number via PowerShell.
Get-msoluser -UserPrincipalName "email#emailaddress.com" | Select-Object -ExpandProperty StrongAuthenticationUserDetails
That code returns this info:
ExtensionData : System.Runtime.Serialization.ExtensionDataObject
AlternativePhoneNumber :
Email :
OldPin :
PhoneNumber : +1 5554445555
Pin :
However, there seems to be no similar option for setting the StrongAuthenticationUserDetails.
All my searches just turned up how to bulk enable 2-factor authentication, which is not what I want to do. I want to leave the StrongAuthentication the same while only updating the phone number.
As I said in comment, it appears there is read-only access for powershell.
There is even opened ticket for that on Azure feedback.
There is a plan to do it, but no ETA. My guess is that you will have to wait if you want to use powershell only.
As workaround, you could use powershell & watir for .NET OR Watin with Watin recorder to automatize it via Internet Explorer. As I don't have a testing Azure; I can not create workable code for you.
Using Watin and powershell - you could check: https://cmille19.wordpress.com/2009/09/01/internet-explorer-automation-with-watin/
The following text and code, I wanted to backup it here, was taken from the above page (all credits to the author):
Next click the record button and click the HTML element you want to
automate. Then stop the WatIN recorder and click copy code to
clipboard icon. This will produce some C# code that just needs to be
translated into PowerShell:
// Windows
WatiN.Core.IE window = new WatiN.Core.IE();
// Frames
Frame frame_sd_scoreboard = window.Frame(Find.ByName("sd") && Find.ByName("scoreboard"));
// Model
Element __imgBtn0_button = frame_sd_scoreboard.Element(Find.ByName("imgBtn0_button"));
// Code
__imgBtn0_button.Click();
window.Dispose();
So, I now know the name of the button and that it is 3 frames deep. A
little WatIN object exploration later, I came up with the follow
script, which clicks a button every 50 mintues.
#Requires -version 2.0
#powershell.exe -STA
[Reflection.Assembly]::LoadFrom( "$ProfileDirLibrariesWatiN.Core.dll" ) | out-null
$ie = new-object WatiN.Core.IE("https://sd.acme.com/CAisd/pdmweb.exe")
$scoreboard = $ie.frames | foreach {$_.frames } | where {$_.name –eq ‘sd’} | foreach {$_.frames } | where {$_.name –eq ‘scoreboard’}
$button = $scoreboard.Element("imgBtn0_button")
while ($true)
{
$button.Click()
#Sleep for 50 minutes
[System.Threading.Thread]::Sleep(3000000)
}
Disclaimer: the code is provided as-is. It might happen that it'll stop working in case MS changes Azure Portal interface.
I'm using the following Greasemonkey script to update alternate email and phone (phone can be updated via Graph API now so the script will be useful for email only):
// ==UserScript==
// #name Unnamed Script 548177
// #version 1
// #grant none
// #namespace https://portal.azure.com
// ==/UserScript==
(function(){
document.addEventListener('keydown', function(e) {
// press alt+shift+g
if (e.keyCode == 71 && e.shiftKey && !e.ctrlKey && e.altKey && !e.metaKey) {
const url = document.URL;
const regex = /https:\/\/portal.azure.com\/#blade\/Microsoft_AAD_IAM\/UserDetailsMenuBlade\/UserAuthMethods\/userId\/[\w-]+\/adminUnitObjectId[\/]*\?\w+=(\d{9})&\w+=([\w\.-#]+)/;
const params = url.match(regex);
const allAuthRows = document.getElementsByClassName('ext-userauthenticationmethods-section-row');
const authRowsArray = Array.from(allAuthRows);
let emailRow;
let phoneRow;
let i;
for (i =0; i < authRowsArray.length; i++) {
if (authRowsArray[i].childNodes[1].childNodes[1].childNodes[0].data === 'Email') {
emailRow = authRowsArray[i]
}
if (authRowsArray[i].childNodes[1].childNodes[1].childNodes.length > 1) {
if (authRowsArray[i].childNodes[1].childNodes[1].childNodes[1].childNodes[0].data === 'Phone') {
phoneRow = authRowsArray[i]
}
}
}
const emailInput = emailRow.childNodes[3].childNodes[1].childNodes[1].childNodes[0].childNodes[0].childNodes[0].childNodes[2];
const phoneInput = phoneRow.childNodes[3].childNodes[1].childNodes[1].childNodes[0].childNodes[0].childNodes[0].childNodes[2];
const event = new Event('input', {
'bubbles': true,
'cancelable': true
});
if (params[1] !== '000000000') {
phoneInput.value = `+48 ${params[1]}`;
phoneInput.dispatchEvent(event);
}
if (params[2] !== 'null') {
emailInput.value = params[2];
emailInput.dispatchEvent(event);
}
setTimeout(() => {
const buttonArr = document.getElementsByClassName('azc-toolbarButton-container fxs-portal-hover');
const saveButton = Array.from(buttonArr).find(e => e.title === 'Save');
saveButton.click();
} , 2000);
}
}, false);
})();
It requires you to open Azure portal with querystring like this (I do it with PowerShell):
https://portal.azure.com/#blade/Microsoft_AAD_IAM/UserDetailsMenuBlade/UserAuthMethods/userId/$($u.ObjectId)/adminUnitObjectId/?param1=$newPhone&param2=$newMail
How to use it:
open only one tab at a time, otherwise you'll receive Unable to sign-in error
from time to time you'll receive that error anyway, so just wait
to trigger the script press Alt+Shift+g after the site is loaded (you can change the shortcut in first if)
once the data is updated and saved, press Ctrl+w to close the current tab and press Alt+Tab to switch to previous window (should be PowerShell)
you're still free to use the code to update the phone. Make sure to change country code (currently +48 for Poland)

Appending variable to string to pull value of existing variable

I'm currently learning PowerShell and I can't work out how to combine a string and a variable to pull information from an existing variable.
The user input will just be a number, so 1,2,3 etc. which I need to append to the end of $option which will pull the title information from the variable $optionX.
So far everything I've tried just interprets it as a string and print $OptionX into the console, as opposed to the value held by $OptionX.
So for example:
function Title{
Write-host "$OptionName for:"$computerSystem.Name -BackgroundColor DarkCyan
}
function GetMenu {
# Set the menu options
$Option1 = "1) System Information"
# Get menu selection
$Navigation = Read-Host "Enter Selection"
ToolBox
}
function ToolBox{
Clear-Host
switch ($Navigation){
1 { #Script 1
Title
}
You can do what you do in the self-answer. I would suggest using a hash-map for it though - seems cleaner to me. (I have no idea what the $computersystem.Name-part is, so I just left it in):
function Title{
Write-host "$($Options[$Navigation]) for:"$computerSystem.Name -BackgroundColor DarkCyan
}
function GetMenu {
# Set the menu options
$Options = #{
"1" = "1) System Information"
"2" = "2) Something else"
}
# Get menu selection
$Navigation = Read-Host "Enter Selection"
ToolBox
}
function ToolBox{
Clear-Host
switch ($Navigation){
1 { #Script 1
Title
}
}
}
For the rest of your script I can see that you are using Global Variables extensively, which I would avoid (it will confuse you, makes it harder to understand what is going on, and many other reasons not to use them). Look into using parameters for your functions, using the snippet menu (CTRL+J) in Powershell ISE will make a quick function skeleton for you. When you want to develop further in Powershell functions look into the Cmdlet (advanced function) template in the same menu.
I figured out how to do it, I'm not sure if it's the best method but it does what I need it to do.
function Title{
$OptionCombine = "Option"+$Navigation
$OptionName = Get-variable $OptionCombine -ValueOnly
Write-host "$OptionName for:"$computerSystem.Name -BackgroundColor DarkCyan
}

NuGet: How can I set "visible" of content to false with Install.ps1 file?

I know this sets CopyToOutputDirectory to Always
$project.ProjectItems.Item("test.exe").Properties.Item("CopyToOutputDirectory").Value = 1
But when I try to set Visible in the same way it won't work
$project.ProjectItems.Item("test.exe").Properties.Item("Visible").Value = 1 or "false" or $false
test.exe does not have a property item Visible so you cannot set the value
You will get an error if you do this
$project.ProjectItems.Item("Program.cs").Properties.Item("Visible")
This will work because CopyToOutputDirectory is an actual property
$project.ProjectItems.Item("test.exe").Properties.Item("CopyToOutputDirectory")
You could use Microsoft.Build.Evaluation.ProjectCollection as an alternative, but making changes in this way would require the project to be reloaded after the package is installed.
$buildProject = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName)
$buildProject.Xml.AllChildren|?{$_.Include -eq "test.exe"} | % {
if ($_.HasMetadata -and ($visible=$_.Metadata|?{$_.Name -eq "Visible"}))
{
$visible.Value = $false
}
else
{
$_.AddMetadata("Visible",$false)
}
}
$buildProject.Save()
However, it appears to prevent the file from being uninstalled, and I'm not sure Uninstall.ps1 is called at the appropriate time for visibility to be restored so that NuGet can handle deletion.