I got a problem with a powershell-script used in our domain to create new users:
Helpdesk will call a .bat as administrator, this bat calls a script-file to automate the creation. In this script, two sessions are created and imported, to use the Exchange- and AD-cmdlets locally.
During/after the import a second/third credential-mask gets thrown, but clicking "cancel" will do nothin, the script will run through without any issues. Nevertheless this annoys the helpdesk..
When running the .ps1 directly from the ISE, the mask won't be shown. Also, when C&Ping the Create-/Import part of the script to a new file and calling it the same way as before also won't show these mask..
Here a part of the .ps1-file:
<#
.DESCRIPTION
Creates a new standard user
.NOTES
Requires : Exchange 2016 Remote Session
Req.OS Version : not tested
Req.PS Version : not tested
.EXAMPLE
Create-User.ps1 -datapath \\path\to\userdata.csv -credentialobject $cred
#>
Param (
[string]$datapath, <#Folder where the CSVs sit #>
[System.Management.Automation.CredentialAttribute()]$credentialobject = $null
)
#region SET global var definitions
$ErrorActionPreference = "Continue"
Start-Transcript -path $ScriptLogPath # | out-null
#endregion
#region SET var definitions
$userfile = "$datapath\userdata.txt"
$groupfile = "$datapath\groupdata.txt"
#Exchange
$MSXremotingserver = "exchangehostname"
$MSXdatabasenames = #("msx_db")
#AD
$domaincontroller = "dchostname"
$ADremotingserver = $domaincontroller
$BaseDN = "OU=Users,DC=domain,DC=local"
#endregion
#region Import Userdata
# CSV's are getting imported here - WORKING
#endregion
#region INIT Remotesession
#Get AD Creds / use given AD Creds
if (($credentialobject -ne $null) -and (($credentialobject.GetType()).name -eq "PSCredential")){
$UserCredential = $credentialobject
}else{
$UserCredential = Get-Credential
# Get credentials to create the remote-sessions. Seems to be working.
}
$MSXSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI http://$MSXremotingserver/powershell -Credential $UserCredential
echo "import..."
$null = Import-PSSession $MSXSession -AllowClobber -DisableNameChecking # | out-null
# After the import (Progress bars running through on top of the PS) another credential-mask appearing, "Cancel" makes the script run through without further errors.
echo "OK"
$ADSession = New-PSsession -Computername $ADremotingserver -Credential $UserCredential
Invoke-Command -Command {Import-Module ActiveDirectory -DisableNameChecking} -Session $ADSession # | out-null
echo "import..."
Import-PSSession -Session $ADSession -Module "ActiveDirectory" -Prefix Remote -AllowClobber -DisableNameChecking # | out-null
# After the import (Progress bars running through on top of the PS) another credential-mask appearing, "Cancel" makes the script run through without further errors.
echo "OK"
#AD-user already existing?
if ([bool](get-remoteaduser -LDAPFilter "(SamAccountName=$($userdata.Kuerzel))")){
#Throw custom error - AD-User bereits vorhanden!
}
#build Account...
# AD-user and Mailbox are created and configured. WORKING!
#endregion
#region END Script
Get-PSSession | Remove-PSSession
Stop-Transcript
Write-Host "Beende Skript..."
start-sleep -Seconds 3
exit 10000
#endregion
And here's how the .ps1 is being called:
%systemroot%\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file \\helpdeskserver\powershell_userdata$\Create-User.ps1 \\helpdeskserver\path\to\csv"
I don't know what to do. Tried many different versions of each command, tried piping the in/output, nothing will do..
Google doesn't seem to know that behaviour, neither anyone here on Stackoverflow..
Thanks for any tips and help, I'll apprechiate!
Regards, Ting3l
Edit: When starting the .bat-file without administrative rights (Or with right-click -> other user.. -> admin-account) the second/third credential-dialog won't appear, instead I get an "Index out of range"-exception.
Maybe it's unimportant, but you can try to exit the session by Exit-PSSession. After that use exit 1000. Becoaser when you use exit in the session it completes the session (wherein all code after will be ignored, but script will have successful completed)
Related
I'm trying to catch the exitcode from a PowerShell script that uses a Invoke-Command to run a scriptblock on a remote machine.
First the BAT file:
The BAT file is run with a variable. The script looks like this:
powershell.exe -noninteractive -noprofile -command "& {E:\Scripts\Check-Services_XXX.ps1 %1 }"
EXIT /B %errorlevel%
The PowerShell script looks like this:
param(
[string] $ip #IP address van server
)
$username = "DOMAIN\DOMAIN_USER"
$secpasswdfile = "E:\Location\DOMAINUSER_encrypted_password.txt"
$secpasswd = Get-Content $secpasswdfile | ConvertTo-SecureString
$credentials = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
$soptions = New-PSSessionOption -SkipCACheck -SkipRevocationCheck -SkipCNCheck
Invoke-Command -ComputerName $ip -UseSSL -SessionOption $soptions -Credential $credentials -ScriptBlock `
{
# Start services
Start-Service -InputObject (Get-Service -Name IAS)
# Check services status
$checkservice = (get-service -Name IAS -ErrorAction SilentlyContinue)
if($checkservice.status -ne "Running"){$host.SetShouldExit(1)}
exit
}
The problem is that the ExitCode is not captured back, so when the BAT file ends, it ends with 0. That would be the case if everything is running. But i deliberately changed the service name in the check service section to something that does not exist for sure, but still it the BAT file ends with Exitcode 0
Done so far: Tried this solution:
catching return code of a command with "invoke-command" - Powershell 2
But didn't work: got the following error "is not equal to Open, you cannot run a command in the session. The session state is Closing"
Apparently, when it exited with a error, the session was closed, thus couldn't get the exitcode
Also tried this one: Capture Write-Host output and exit code from Invoke-Command on a Remote System
But also the same result; no correct exitcode (expected 1 instead of 0 in the BAT file)
SOLUTION!
Thanks to #js2010 and #mklement0 ; it works now like a charm!
This is the BAT file:
powershell.exe -noprofile -File "E:\Scripts\Check-Services_XXX.ps1" "%1" "%2"
EXIT /B %errorlevel%
And here is the PowerShell code that eventually worked out for me:
param(
[string] $ip, #IP address of checked server
[string] $service ) #Service name
$username = "DOMAIN\USER"
$secpasswdfile = "E:\Scripts\Credentials\DOMAIN-USER_encrypted_password.txt"
$secpasswd = Get-Content $secpasswdfile | ConvertTo-SecureString
$credentials = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
$soptions = New-PSSessionOption -SkipCACheck -SkipRevocationCheck -SkipCNCheck
$session = New-PSSession -ComputerName $ip -UseSSL -SessionOption $soptions -Credential $credentials
# Start services
Invoke-Command -Session $session -ScriptBlock { Start-Service -Name $using:service }
# Check services status
$checkservice = Invoke-Command -Session $session { Get-Service -name $using:service | where status -eq running }
if (! $checkservice) {
write-output ("Error 1, Service '" + $service + "' not running or not found.")
exit 1
}
I had some issues with passing variables to remote commands, this link helped me out (https://powershellexplained.com/2016-08-28-PowerShell-variables-to-remote-commands/)
You would have to run the exit command outside of invoke-command.
# check-service.ps1
$result = invoke-command localhost { get-service appxsvc |
where status -eq running }
if (! $result) {
exit 1
}
Change your invocation of powershell.exe to use the -File CLI parameter:
powershell.exe -NoProfile -File "E:\Scripts\Check-Services_XXX.ps1" "%1"
EXIT /B %errorlevel%
That way, the .ps1 script's exit code is properly relayed as powershell.exe's exit code.
Additionally, as js2010's answer notes, you'll need to use your $host.SetShouldExit(1) call out of the Invoke-Command script block, given that the latter executes remotely. For the reasons explained below, exit 1 is preferable.
Generally speaking:
There's no reason to use the -Command (-c) CLI parameter with "& { ... }" in order to invoke code - just use "..." directly. Older versions of the CLI documentation erroneously suggested that & { ... } is required, but this has since been corrected.
Not only is "& { ... }" unnecessary, it invariably resets the exit code to 0.
As for your use of $host.SetShouldExit(1) to request exiting with an exit code of 1 (leaving aside that in a remote call it isn't effective):
It generally isn't designed to be called from user code, as explained in this answer.
For general information about exit codes in PowerShell, see this answer.
I am trying to connect to PowerShell using the Connect-ExchangeOnline command but I receive the following error. Any ideas?
New-ExoPSSession : .
At C:\Program Files\WindowsPowerShell\Modules\ExchangeOnlineManagement\1.0.1\ExchangeOnlineManagement.psm1:445 char:30
+ ... PSSession = New-ExoPSSession -ExchangeEnvironmentName $ExchangeEnviro ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [New-ExoPSSession], Exception
+ FullyQualifiedErrorId : System.Exception,Microsoft.Exchange.Management.ExoPowershellSnapin.NewExoPSSession
Please try to use the cmdlet as follows:
Connect-IPPSSession -PSSessionOption
$EXOSession = New-ExoPSSession -pssessionoption
Import-PSSession $EXOSession -Prefix EXO
Removing the security baseline from Intune and resyncing by rebooting fixed the issue in my case.
Here's a script that I use to connect. Please fill in your default username and make sure to run the prerequisites in the comments at the top (review first of course)
WMF 5.1
.Net 4.5
And if you're using this non-interactive you'll want to look up how to store your credentials securely and how to use those stored credentials, replacing the Get-Credential section with your configuration
# Requires: .Net 4.5, Windows Management Framework 5.1 (see https://learn.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps )
#
# Run Once:
# Set-ExecutionPolicy RemoteSigned; Get-ExecutionPolicy
# Install-Module PowerShellGet -Force
# Install-Module –Name ExchangeOnlineManagement
#
# https://learn.microsoft.com/en-us/powershell/exchange/exchange-online/exchange-online-powershell-v2/exchange-online-powershell-v2?view=exchange-ps
$exo = New-Module -AsCustomObject -ScriptBlock {
$UserName = "Default.Username#domain"
$UserCredential = Get-Credential -message "Enter 365 admin credentials" -UserName $UserName
function IsConnected(){
try{
if (
#($(get-mailbox -resultsize 1 -WarningAction silentlycontinue)).count `
-eq
1
) {return $true}
}
catch {}
return $false
}
function Connect(){
$result = "Unfinished"
if ($this.IsConnected()) {
$result = "Success"
} else {
$UserCredential = $this.UserCredential
Connect-ExchangeOnline -Credential $UserCredential
if ($this.IsConnected()) {
$result = "Success"
} else {
$result = "Fail"
}
}
switch($result){
"Unfinished" {Write-Warning "`nAn unknown error occured in .Connect(), Appears to have ended while unfinished";break}
"Success" {Write-host "`nSuccessfully connected to Exchange 365";break}
"Fail" {Write-Warning "`nFailed to connect to Exchange 365";break}
default {write-warning "`nAn unknown error occured in .Connect(), result code unrecognized";break}
}
# old style
#$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
#Import-PSSession $Session -DisableNameChecking -AllowClobber
}
function ConnectMsol(){
Connect-MsolService -Credential $UserCredential
}
function Disconnect(){
Disconnect-ExchangeOnline
# old sytle
#Remove-PSSession $Session
}
function Cycle(){
$this.Disconnect()
$this.Connect()
}
Export-ModuleMember -Function * -Variable *
}
$exo.Connect()
#$exo.ConnectMsol()
<#
# --- Azure AD ---
# https://learn.microsoft.com/en-us/office365/enterprise/powershell/connect-to-office-365-powershell#connect-with-the-azure-active-directory-powershell-for-graph-module
# --- Azure AD ---
#>
The newest version of Exchange Online module requires PowerShell V7. As soon as I installed V7 it worked fine.
Per: https://learn.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps#install-and-maintain-the-exo-v2-module
EXO V2 module is supported in PowerShell 7.0.3 or later
I was able to resolve a similar issue by switching my default web browser in Windows 10 to the Chromium Edge browser. I brought up the login window once, then switched Firefox back to my default browser and it still worked.
PowerShell v7 is not required to use the ExchangeOnlineManagement module, but is an acceptable workaround. It uses the account logged into the default web browser for authentication, which wasn't an acceptable solution for me. It led me to switching the default browser, though, which resolved my initial problem.
Context
I created a little Powershell Script that lets you change between 2 Exchange Server (Pssession to the 2 servers).
In the Begin part of the script I remove all existing pssessions (it's on to the default Exchange 2010 server).
# Remove all Ps-Sesssions
Get-PSSession | Remove-PSSession
Then in the Process part I do this:
Process
{
# Choose Server:
# Default server
$server = "2010"
if ($ExchangeVersion -eq "2016") {
$server = "2217ex0010at01"
} elseif ($ExchangeVersion -eq "2010") {
$server = "2217exlimbx01"
} else {
Write-Host "Server wurde nicht gefunden, gibt 2010 oder 2016 ein"
}
# Add Pssession and import it
$s = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$server/powershell
Import-PSSession $s -AllowClobber
}
The problem
When I start up powershell, it imports a pssession to the 2010 server. Everything is working fine. I have all commandlets like get-mailbox
But when I run the script like this Set-ExchangeServer -ExchangeVersion "2016" everything appears to be fine. But then when I try any exchange commandlet it won't exist.
What have I tried
I manually typed in the code I have in the script to import the session to 2016. And thats the weird part. When I'm doing it manually it works and I have all commandlets.
What could be the cause of this issue?
I did for myself simple functions to remove the previous exchange sessions, it might help you.
Probably you did'nt remove the tmp*... Module...
For On-Premise:
Function Remove-LocalExchangeShell
{
if ($LocalSession = Get-PSSession | ? {$_.ComputerName -match 'exchSvrName'}) {Remove-PSSession $LocalSession}
if ($TmpModule = Get-Module -Name tmp*) {Remove-Module $TmpModule}
}
For Office365:
Function Remove-365ExchangeShell
{
if ($365Session = Get-PSSession | ? {$_.ComputerName -match 'Outlook'}) {Remove-PSSession $365Session}
if ($TmpModule = Get-Module -Name tmp*) {Remove-Module $TmpModule}
}
Run the one you need before executing the New-PSSession
I think the root cause is a question of scope.
Try changing $s = to $global:s = so that the session is available outside of the script.
The variable at the top of the script defines several commands/variables for New-PSDrive, as well as connection and installation.
After this, a function is created to open a text file and extract information out of it. I know this part works because I use it in 2 other scripts.
Lastly, The script executes the commands in the first variable.
The script will show as running successfully, but checking the remote computer reveals that nothing happened.
Prior to doing any of this activity, the remote computer has a script run against it that:
enables PSRemoting (setting firewall rules and starting WinRM), and
bypasses execution policies.
After those steps, the script below is run to install a piece of software.
$eAudIT2014V2Install = {
$eAudIT2014V2password = ConvertTo-SecureString "PasswordHere" -AsPlainText -Force
$eAudIT2014V2cred = New-Object System.Management.Automation.PSCredential('domain\user', $eAudIT2014V2password)
$eAudIT2014V2drive = New-PSDrive -Name eAudIT2014V2 -PSProvider FileSystem -Root "\\Server\Share" -Credential $eAudIT2014V2cred
$eAudIT2014V2job = Start-Job {"eAudIT2014V2:\Setup.cmd"}
Wait-Job $eAudIT2014V2job
Receive-Job $eAudIT2014V2job
}
Function Get-OpenFile($initialDirectory) {
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.InitialDirectory = $initialDirectory
$OpenFileDialog.ShowDialog()
$OpenFileDialog.Filename
$OpenFileDialog.ShowHelp = $true
}
$InputFile = Get-OpenFile
if ($InputFile -eq "Cancel") {
Write-Host "Canceled By User"
exit
} else {
$Computers = #(Get-Content -Path $InputFile)
}
foreach ($computer in $computers) {
Write-Host "Installing eAudIT 2014V2 on Selected Computers"
Invoke-Command $eAudIT2014V2Install
}
I'm noticing that if I tell this script to run something basic like notepad.exe, a dllhost process starts on the machine, but notepad never does. What am I doing wrong?
The answer is pretty simple here. All of your script is for naught if you don't tell the Invoke-Command cmdlet what computer you want to execute the code on. As it is you are simply iterating a loop and invoking that command X number of times on the local machine. You need to change that second to the last line to specify the machine to execute the code on:
Invoke-Command $eAudIT2014V2Install -ComputerName $computer
I'm trying to send this:
Get-WmiObject Win32_PNPEntity |Where{$_.DeviceID.StartsWith("PCI\VEN_10DE") -or $_.DeviceID.StartsWith("PCI\VEN_1002")}
over rdesktop like:
rdesktop -a8 209.** -u ** -p ** -s "cmd.exe /K powershell.exe Get-WmiObject Win32_PNPEntity |Where{\$_.DeviceID.StartsWith("PCI\VEN_10DE") -or $_.DeviceID.StartsWith("PCI\VEN_1002")}"
But windows' shell says:
'Where{$_.DeviceID.StartsWith' is not recognized as an internal or externa....
What am I doing wrong?
why not using powershell wmi remoting?
$cred = get-credential
Get-WmiObject Win32_PNPEntity -computerName MyRemoteComputerName - credential $cred |Where{$_.DeviceID.StartsWith("PCI\VEN_10DE") -or $_.DeviceID.StartsWith("PCI\VEN_1002")}
-credential are only needed if the actual user running powershell isn't administrator of remote machine.
Hi I needed to do some thing like this once so i wrote some code that can send any ps code to a remote computes and display the results in the ps window on your pc.
Just remember to enable powershell remoting on both pc's.
function remote-pscode ($ServerName,$UserName,$password,$PSCode)
{
$global:RemoteCode = $args[0]
Write-Host $RemoteCode
$conprops = (Get-Host).UI.RawUI
$buffsize = $conprops.BufferSize
$buffsize.Height = 800
$conprops.BufferSize= $buffsize
# Set the user name you would like to use for the connection
$global:RemoteUserName = $UserName
$global:RemoteServerName = $ServerName
# Set the password you would like to use for the connection
# Check to see if you have a file on you drive c:\cred.txt with a password to use in it,if you don't it will create one
# for you and ask you for the password you would like to use
$global:RemotePassword = convertto-securestring $password -AsPlainText -Force
$global:credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $RemoteUserName,$RemotePassword
#Create a connection to the remote computer , put a list of IPAddresses or Computer Names.
$global:session = new-PSSession -ComputerName $RemoteServerName -Credential $credentials
$ScriptBlock = $executioncontext.invokecommand.NewScriptBlock($RemoteCode)
invoke-command -Session $session -ScriptBlock $ScriptBlock
#Close the sessions that where created
$global:closesession = Get-PSSession
Remove-PSSession -Session $closesession
}
remote-pscode -ServerName "NameOfRemotePC" -UserName "UserName" -password "password" -PSCode "any powershell code you want to send to the remote pc"
Several things here: put your PS commands in a script block (or a script). Also, why don't you simply use wmic.exe ?