This is my first question here and I am also quite new on PowerShell, so I hope I am doing everything alright.
My problem is the following: I want to uninstall a programm on several computers, check if the registry-key is deleted and then install a new version of the programm.
The setup is located on a server within the same domain as the computers.
I want my Script to loop through the computers and execute the setup from the server for every computer. As I am quite new with PowerShell, I have no idea how to do this. I was thinking to maybe use Copy-Item, but I dont want to really move the setup, but simply execute it from the server to the computers? Any idea how to do this?
Best regards
You can try the following approach.
Note that the need to provide credentials explicitly is a workaround for the infamous double-hop problem.
# The list of computers on which to run the setup program.
$remoteComputers = 'computer1', 'computer2' # ...
# The full UNC path of the setup program.
$setupExePath = '\\server\somepath\setup.exe'
# Obtain credentials that can be used on the
# remote computers to access the share on which
# the setup program is located.
$creds = Get-Credential
# Run the setup program on all remote computers.
Invoke-Command -ComputerName $remoteComputers {
# WORKAROUND FOR THE DOUBLE-HOP PROBLEM:
# Map the target network share as a dummy PS drive using the passed-through
# credentials.
# You may - but needn't - use this drive; the mere fact of having established
# a drive with valid credentials makes the network location accessible in the
# session, even with direct use of UNC paths.
$null = New-PSDrive -Credential $using:cred dummy -Root (Split-Path -Parent $using:$setupExePath) -PSProvider FileSystem
# Invoke the setup program from the UNC share.
& $using:$setupExePath
# ... do other things
}
Related
I am attempting to automate the Skype for Business Server installation process in Powershell, I have a script that remotes into specified machines and begins preparing them as Front-End servers. The problem lies when certain SfB cmdlets (SfB commands are all of the form "verb-Cs...", ex. Get-CsUser or Get-CsPool) are run in remote sessions, they throw the double-hop error:
Exception: Active Directory error "-2147016672" occurred while searching for domain controllers in domain...
This is after running Enable-CsComputer, which enables the computer's role-based off its definition in the topology (topology was published successfully). The user object is in all required groups (RTCUniversalServerAdmins, Schema Admins, CsAdministrators & Local Admin rights on all SfB Servers). Oddly enough, the command 'Import-CsConfiguration -localstore" does not throw errors, and it's in the same remote session. There may be other local or domain groups that I need to be in, but I cannot pinpoint exactly which and have not seen them documented in the Skype build guides. Skype commands that have parameters to specify targets or just pull data, such as Get-CsPool or Get-CsAdForest, do not have errors because they are run in the local scope. The Enable-CsComputer has no parameter for the computer name, it has to be executed from that machine itself.
Enabling CredSSP delegation on each server is not an option, and I'm not understanding why there is a "second hop" in this command! If the second hop was a resource on a file server or database, that would make sense, and be easy to solve, but in this case, I can't track it. Can anyone tell me what I may be missing?
Here's a code sample to try and illustrate. From the jumbox I get the pool data to create an array, and a session is opened to each machine:
$ServerArray =get-cspool -identity $poolName
$i=0
$SessionArray = #{}
foreach($server in $ServerArray.Computers){$SessionArray[$i] = new-PsSession -ComputerName $server}
foreach($session in $SessionArray.values){
invoke-Command -session $session -scriptBlock {
#remote commands:
import-csConfiguration -<config file path> -localstore; #no errors
enable-CsReplica; #no errors
enable-cscomputer; #double hop error here
}}
If I log into that machine and run the same command, it executes fine but the intention of the project is to automate it on an arbitrary number of machines.
It looks like it's just trying to authenticate to a domain controller, which is reasonable. You'll have to approach this like any other double-hop issue.
Microsoft has an article dedicated to the double hop issue, and has a few solutions other than CredSSP that you can look at: Making the second hop in PowerShell Remoting
I am currently building a Hyper-V lab consisting of a DC and multiple networked VMs, using Windows Server 2016. I'd like to completely disable the windows firewall for all existing and newly created VMs.
The best way that I've found to do this so far is via Group Policy for the Domain Profile. Then set Windows Firewall: Protect all network connections to disabled. What I would like to do is to have a way of scripting this out (using Powershell if possible).
I've found that by performing the above steps in the GUI, it creates a few entries in the registry:
HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\WindowsFirewall\DomainProfile
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Policies\Microsoft\WindowsFirewall\DomainProfile
In each of those entries, there is a property called EnableFirewall which is set to 0. So I tried creating all of this using Powershell like this:
New-Item -path "HKLM:\SOFTWARE\Policies\Microsoft" -name WindowsFirewall
New-Item -path "HKLM:\SOFTWARE\Policies\Microsoft\WindowsFirewall" -name DomainProfile
New-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\WindowsFirewall\DomainProfile" -name EnableFirewall -value 0 -PropertyType DWord -Force
Unfortunately it doesn't seem to be working, so there must be something else that I'm missing.
Does anybody know how to completely disable the windows firewall for all networked machines using the command line in Windows Server 2016?
Setting up the Windows-Firewall for your domain-computers through computer-startup-script is not a great solution in my opinion.
You should definetly use Group Policy for this task.
GP does exactly what I want, I would just like a way of modifying GP using Powershell. I'm building a lab from scratch, and I'm looking to script as much of it as possible rather than using the gui.
I am not completely sure, what you are trying to achive.
You have created a lab now and I think you are trying to script a complete automatic built-up for future use. Is this correct?
If yes, then my solution is maybe what you are looking for:
Create a new GPO in your lab named "Firewall-Settings" for example.
Make all of your needed FireWall-Settings to the new GPO.
In Group Policy Editor open the main-node named „Group Policy Objects“. (important) Find the newly created GPO, right-click it and select "Backup":
Save the GPO-backup to a folder. (folder must exist)
The GPO is beeing saved and named like on the screenshot below (GUID):
That's it for the preparation. Now you maybe want to script the creation of the GPO with Powershell for future use and import the backup to obtain it's settings in a new environment:
New-GPO -Name "FireWall-Settings" | New-GPLink -Target "DC=mydomain,DC=local" # distinguishedName of Target-OU
Import-GPO -Path $PathtoGPOBackup -TargetName "FireWall-Settings" -BackupGpoName "FireWall-Settings"
The Script creates a GPO in the new environment with the name "FireWall-Settings" and links it to the target-OU.
After that you import the settings of the backup-GPO. All the domain-members in scope of the GPO will get the Windows-Firewall configured automatically.
Now the process is documented and fully automatic, if this is, what you are looking for.
Kind regards
open cmd prompt with elevated mode and run this:
netsh -r ComputerName -u Username -p Password -c advfirewall set allprofiles state off
If you want to do it for all the machines. Get all the ad computers using get-adcomputer. Run a foreach loop and put the variable istead of computername.
If you have the domain admin creds, then you are good to go with this.
Hope it helps.
Depending on the profile you want to disable, specify profiles (public, domain, private) using the -Name parameter. To disable all profiles for a networked machine, where $computerName array is the hostname of your DC, PC etc:
$computerName = 'DC1, PC1, MS1'
Invoke-Command -Computername $computerName -ScriptBlock {
Set-NetFirewallProfile -Name Domain, Public, Private -Enabled False
}
I am running into an interesting issue. Maybe you fine folks can help me understand what's happening here. If there's a better method, I'm all ears.
I am running a DSC Configuration on Azure and would like to map a drive. I've read this really isn't what DSC is for, but I am not aware of any other way of doing this outside of DSC with Azure Scalesets. Here's the portion of the script I am running into issues:
Script MappedDrive
{
SetScript =
{
$pass = "passwordhere" | ConvertTo-SecureString -AsPlainText -force
$user = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "username",$pass
New-PSDrive -Name W -PSProvider FileSystem -root \\azurestorage.file.core.windows.net\storage -Credential $user -Persist
}
TestScript =
{
Test-Path -path "W:"
}
GetScript =
{
$hashresults = #{}
$hashresults['Exists'] = test-path W:
}
}
I've also attempted this code in the SetScript section:
(New-Object -ComObject WScript.Network).MapNetworkDrive('W:','\\azurestorage.file.core.windows.net\storage',$true,'username','passwordhere')
I've also tried a simple net use command to map the drive instead of the fancy, New-Object or New-PSDrive cmdlets. Same behavior.
If I run these commands (New-Object/Net Use/New-PSDrive) manually, the machine will map the drive if I run it with a separate drive letter. Somehow, the drive is attempting to be mapped but isn't mapping.
Troubleshooting I've done:
There is no domain in my environment. I am simply attempting to create a scale set and run DSC to configure the machine using the storage account credentials granted upon creation of the storage account.
I am using the username and password that is given to me by the Storage Account user id and access key (randomly generated key, with usually the name of the storage account as the user).
Azure throws no errors on running the DSC module (No errors in Event Log, Information Only - Resource execution sequence properly lists all of my sequences in the DSC file.)
When I log into the machine and check to see if the drive is mapped, I run into a disconnected network drive on the drive letter I want (W:).
If I open Powershell, I receive an error: "Attempting to perform the InitializeDefaultDrives operation on the 'FileSystem' provider failed."
If I run "Get-PSDrive" the W: drive does not appear.
If I run the SetScript code manually inside a Powershell Console, the mapped drive works fine under a different drive letter.
If I try to disconnect the W: drive, I receive "This network connection does not exist."
I thought maybe DSC needed some time before mapping and added a Sleep Timer, but that didn't work. Same behavior.
I had a similar problem before, while it didn't involve DSC, mounting an Azure File share would be fine until the server would be restarted, then it would appear as a disconnected drive. This happend if i used New-Object/Net Use/New-PSDrive with the persist option.
The answer to that issue, i found in the updated docs
Persist your storage account credentials for the virtual machine
Before mounting to the file share, first persist your storage account
credentials on the virtual machine. This step allows Windows to
automatically reconnect to the file share when the virtual machine
reboots. To persist your account credentials, run the cmdkey command
from the PowerShell window on the virtual machine. Replace
with the name of your storage account, and
with your storage account key.
cmdkey /add:<storage-account-name>.file.core.windows.net /user:<storage-account-name> /pass:<storage-account-key>
Windows will now reconnect to your file share when the virtual machine
reboots. You can verify that the share has been reconnected by running
the net use command from a PowerShell window.
Note that credentials are persisted only in the context in which
cmdkey runs. If you are developing an application that runs as a
service, you will need to persist your credentials in that context as
well.
Mount the file share using the persisted credentials
Once you have a remote connection to the virtual machine, you can run
the net use command to mount the file share, using the following
syntax. Replace with the name of your storage
account, and with the name of your File storage share.
net use <drive-letter>: \\<storage-account-name>.file.core.windows.net\<share-name>
example :
net use z: \\samples.file.core.windows.net\logs
Since you persisted your storage account credentials in the previous
step, you do not need to provide them with the net use command. If you
have not already persisted your credentials, then include them as a
parameter passed to the net use command, as shown in the following
example.
Edit:
I don't have an Azure VM free to test it on, but this works fine on a Server 2016 hyper-v vm
Script MapAzureShare
{
GetScript =
{
}
TestScript =
{
Test-Path W:
}
SetScript =
{
Invoke-Expression -Command "cmdkey /add:somestorage.file.core.windows.net /user:somestorage /pass:somekey"
Invoke-Expression -Command "net use W: \\somestorage.file.core.windows.net\someshare"
}
PsDscRunAsCredential = $credential
}
In my brief testing the drive would only appear after the server was rebooted.
What I imagine is happening here:
DSC runs under the NT AUTHORITY\SYSTEM account and unless the Credential attribute has been set, the Computer account is used when pulling the files from a network share. But looking at how Azure Files operate, permissions shouldn't be an issue, but running this whole process under NT AUTHORITY\SYSTEM could. I suggest you try to run DSC as a user of your VM's and see if that works.
ps. You could also try to perform the same operation against a VM with network share where you are confident that share\ntfs permissions are correct. You might need to enable anonymous user to access your share for that to work.
I'm fairly new to PowerShell and am running into a problem.
I want to do the following:
Get list of permissions/users on a single folder on a different server than where I am running my PowerShell window from.
Current command failing:
Get-acl -path "\\servername\folder"
Error Message:
Get-acl : Cannot find path '\\servername\folder' because it does not exist
Does this command only work on the local machine?
It turns out with the way permissions/authentications are setup in my environment prevented my code from working.
Here are the steps I took to verify if I could connect to the server:
Test-Path \\server\folder
This returned "False", which is why my code was breaking.
The work around I used was this:
#Step 1: remotely connect to server
Enter-PSSession -ComputerName servernamegoeshere
#Step 2: get list of permissions on folder and save to csv
get-acl E:\foldernamehere |
select -expand access |
export-csv C:\Users\usernamegoeshere\Documents\listofperms.csv |
#Step 3: close remote connection
Exit-PSSession
I still had to remote into the server and copy the csv to the location I wanted because again, any copy command to another server/share in PowerShell would not work due to permission/authentication issues.
This article explains authentication/permissions a bit better than I can:
http://blogs.technet.com/b/heyscriptingguy/archive/2012/11/14/enable-powershell-quot-second-hop-quot-functionality-with-credssp.aspx
Second way to do this with less code and not having to create a remote session thanks to user Ansgar Wiechers:
Invoke-Command -Computer server -ScriptBlock {get-acl E:\folder |
select -expand access } |
export-csv \\server\folder\accesslist.csv
With PowerShell, there are many ways to do one thing...I think this way is best/most simple! Thanks!
The command works on UNC paths as well, but UNC paths are slightly different from local paths. You need an access point to enter the file system of a remote host. For SMB/CIFS access (via UNC paths) that access point is a shared folder, so you need a path \\server\share or \\server\share\path\to\subfolder.
With an admin account you could use the administrative shares (e.g. \\server\C$\Users\Administrator), otherwise you need to create a share first.
I'm doing a pilot effort to use the new EventSource (Microsoft.Diagnostics.Tracing.EventSource from nuget) and its new support for ETW channels in order to write to the windows event log. The code is in place, and it writes properly to my workstations event log. I'm thrilled.
Now comes the difficult part. The application that's taking advantage of this capability is a web service, and we deploy it with webdeploy as part of a build-deploy-test system. Because usage of ETW channels requires static registration of provider manifests via wevtutil.exe. The EventSource documentation states that this is best done as part of an installer, but this seems a bit out of webdeploy's capabilities.
Our aim is that we would be able to automatically uninstall the manifest resident on the target server immediately before executing the webdeploy package, and then to import the new manifest after the webdeploy sync has completed. We're not set on this, but it seems like the most sensible way.
For that reason, it seems like maybe this is something that powershell remoting might be able to solve, but it's not an area I know much about.
Has anyone done something like this? Is there a better or simpler way?
There are only a few requirements here. A) the remote machine must have PowerShell remoting enable which also means it must have PowerShell 2.0 or higher B) the script running on the local machine must be able to run as administrator and the credentials used must have admin privileges on the remote machine. If you can meet those requirements then this should be cake.
On the remote machine you need to execute two commands to enable remoting:
Set-ExecutionPolicy RemoteSigned
Enable-PSRemoting -Force
Then on the local machine from an elevated prompt you should be able to execute something like this from a script:
# these two paths assume these files have been copied to the remote computer and to a directory
# in which the service account has privileges to read i.e. not under a userprofile dir.
$etwDllPath = c:\somepath\myassembly.mysourcename.etwManifest.dll
$etwManPath = c:\somepath\myassembly.mysourcename.etwManifest.man
$s = New-PSSession -ComputerName <remoteComputerName>
Invoke-Command -Session $s {param($man) wevtutil.exe um $man} -arg $etwManPath
Invoke-Command -Session $s {param($man,$dll) wevutil.exe im $man /rf:$dll /mf:$dll} -arg $etwManPath, $etwDllPath
Remove-PSSession $s
If you can avoid a remote path with spaces, try to. It will make this easier. :-)