I am writing a powershell script to be deployed by SCCM via a package. The aim of this is to remove an account with a specific name then write to a file stating if the account exists or not. The code is below:
$Computer = hostname
foreach ($C in $Computer) {
if (Test-Connection $C -Quiet) {
Write-Verbose "$C > Online"
$Users = Get-WMIObject Win32_UserAccount -Filter "LocalAccount=True" -ComputerName $C
if ($Users.Name -contains 'test') {
Add-Content \\SERVERNAME\SHARENAME.$\$computer-found_$(get-date -Format yyyymmdd_hhmmtt).txt "User 'test' found, Disable 'test' found"
net user test /active:no }
else {
Add-Content \\SERVERNAME\SHARENAME.$\$computer-notfound_$(get-date -Format yyyymmdd_hhmmtt).txt "User 'test' not found"
}
}
else {
Write-Verbose "$C > Offline"
}
}
I have also tried replace Write-Verbose with Write-Host and Add-Content with Out-File but the problem I having is that no content / file is created when I use the full network path or share e.g. \\SERVERNAME\SHARENAME.$ the path identified has all the correct permissions and is being ran locally using the System account.
I wanted to see if the issue occured when writing the file locatlly consequently this does not happen when written to C:\Temp\
Does anyone have any ideas on to solve this.
I don't think that local system account has access to a network resource. I'm not sure if you have ever configured it or not. And what the command you used to run the command
Here I post a working way of doing this using Configuration Manager deployment after testing in my lab.
Basically I created a package with source files
and created a task sequence with single "Run Command Line" step.
The reason I use a task sequence is because I want to use an account to access the txt file on the network, which I can configure within a task sequence. I don't think Local System Account have such permission.
The script (DeactivateTest.ps1) I use as below just like what you provided and changed a little on the logic:
$Computer = hostname
foreach ($C in $Computer) {
if (Test-Connection $C -Quiet) {
Write-host "$C > Online"
$Users = Get-WMIObject Win32_UserAccount -Filter "LocalAccount=True" -ComputerName $C
$result=0
Foreach($user in $Users){
if ($User.Name -like '*test*') {
$username = $user.Name
"`n$(get-date -Format yyyymmdd_hhmmtt) User $username found ON $C, Disable 'test'" | Add-Content \\cas\resource\Result.txt
net user $username /active:no
$result+=1
}}
if($result =0){
"`n$(get-date -Format yyyymmdd_hhmmtt) User 'test' not found ON $C" | Add-Content \\cas\resource\Result.txt}
}
else {
"`n$C is Offline" | Add-Content \\cas\resource\Result.txt
}
}
The script query local account and disable accounts which have words "Test" in the name. If you don't like this logic, you can change :).
\\cas\resource\Result.txt is a txt file on the network share. Clients will write result to this txt file.
The command in the task sequence is (it's a x64 machine):
PowerShell.exe -ExecutionPolicy Bypass -File ".\DeactiveTest.ps1"
The output is like:
I may get downvoted for this as my answer isn't technically directly answering your question, it is, however, intended to try and point you in what may be a more logical direction. All apologies if I offend anyone, but here it is:
Why not just disable the user using Group Policy? If you really want to know where the user is/isn't disabled then you could just use hardware inventory for that, but GP really is the best way to enforce this kind of setting.
Related
I am trying to make a script that allows new users on the domain to be put into certain groups based on their tag e.g [DALT]. When I run the script it should work properly as it uses the correct OUs and target path but it seems to not work the way I expect it to. I use a credential saved on my D drive as the cred to have admin rights.
$deviceName = Hostname
$deviceName = $deviceName -replace '[^a-zA-Z]', ''
$defaultName = Hostname
# Import Cred for access to change Dir
$credential = Import-Clixml -Path 'D:\backgroundProcess\cred.xml'
$credential
# Directing users to different part of AD depending on deviceName
# Action House (Branch of Company)
if ($deviceName -eq 'DALT') {
Move-ADObject -Identity "CN=$defaultName,CN=Computers,DC=internal,DC=ttlhidden,DC=co,DC=uk" -TargetPath "OU=Windows,OU=Laptop,OU=Computers,OU=DEKRA,DC=internal,DC=ttlhidden,DC=co,DC=uk"
Write-Host "$defaultName added to Hidden group."
gpupdate /force
}
else {
Write-Host "Sorry, $deviceName is not a verified name, please contact Max for more information."
}
Remove-Item -Path 'cred.xml'
It always seems to render the else option even if my computer has DALT at the start.
I'm brand new to PS scripting, so bear with me :)
I'm trying to create a PS script that will write the Win10 activation code to a file then copy that file to a central repo to then manually activate.
I'm creating a PS script and trying to run
cscript.exe c:\windows\system32\slmgr.vbs -dti >
$SourceDir\$env:computername.txt
$SourceDir = \\computer01\c$\temp
I need to run it from one computer, remotely connecting to every computer on the network, creating the computername.txt file then copying that file back to a central repository for all the files.
What I have so far:
$s1=New-PSSession -ComputerName computer01 -Credential $AdminCred
Test-Connection -ComputerName computer01
$id='\\computer01\windows\system32'
$SourceDir='\\computer01\c$\temp'
md $SourceDir
$GetActID=cscript.exe $id\slmgr.vbs -dti >
$SourceDir\$env:computername.txt
Invoke-Command -Session $s1 -ScriptBlock { $Using:GetActID }
Then I call a batch file that copies the computername.txt file from the computer01 over to a repository where they are going to sit.
I FINALLY got it working correctly except for the name of the file isn't naming it to the computer01, it's naming it with the hostname of the computer I'm running it from, therefore the filenames are identical. I had the naming piece working, but I had to change the way I was remoting into the computer and now it's not naming correctly.
Any idea on how I could get it to name the file to be related to the remote computer?
**I'm still working on the whole piece of the puzzle where it goes back to an excel sheet pulled from AD and pulls the host names from that sheet to connect to each machine, I believe I'll be adding a ForEach syntax in there somehow for that.
Although not sure how you are getting the list of "every computer on the network", chances are you are doing this using
# get a list of all AD computers (their names only)
$computers = (Get-ADComputer -Filter *).Name
Then I think you don't need to have every computer save the file on its own disk and later copy these files to a central share.
Instead, just capture the info in a variable and after the loop write the file to the central share as structured CSV file combining all computernames and install id's so you can open in Excel.
Using the array of computernames from above, iterate through them
$result = $computers | ForEach-Object {
# test if the computer can be reached
if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
$installId = Invoke-Command -ComputerName $_ -ScriptBlock {
cscript.exe //nologo "$env:SystemRoot\System32\slmgr.vbs" -dti
}
# $installId is returned as array !
# output an object with two properties
[PsCustomObject]#{
Computer = $_
InstallId = $installId[0] -replace '\D' # remove everything non-numeric
}
}
else {
Write-Warning "Computer $_ is not responding"
}
}
# now you can display the result on screen
$result | Format-Table -AutoSize
# or by means of the GridView if you prefer
$result | Out-GridView -Title 'Computer InstallIds'
# and save the results in your central share as structured CSV file
$result | Export-Csv -Path '\\server\share\restofpath\ComputerInstallIds.csv' -NoTypeInformation
You may have to append -Credential $adminCreds to the Invoke-Command call to make sure you have permissions to have each machine run that piece of code in the scriptblock. The easiest way of obtaining that credential is to start off with $adminCreds = Get-Credential -Message "Please enter administrator credentials"
More of a basic question but I can't seem to get the syntax correct. I am trying to go through a list and then output the Computer Name and BDE status with it.
Get-Content 'C:\Users\Test\Test\IT\Lists\LaptopList.txt' | ForEach-Object { write-host "***" | manage-bde -status }
Is there something I need to put where the *'s are so that it correlates with the computer/object and the status?
Thanks!
Is there something I need to put where the *'s are so that it correlates with the computer/object and the status?
Yes! You're looking for $_:
Get-Content 'C:\Users\Test\Test\IT\Lists\LaptopList.txt' | ForEach-Object {
Write-Host $_
}
As the name indicates, Write-Host writes output directly to the host application - in the case of powershell.exe it writes directly to the screen buffer - which means it doesn't output anything to downstream cmdlets in a pipeline statement like the one you've constructed.
You'll therefore want two separate statements:
Get-Content 'C:\Users\Test\Test\IT\Lists\LaptopList.txt' | ForEach-Object {
Write-Host $_
manage-bde -status
}
Now, manage-bde is not a PowerShell cmdlet - it's a windows executable, and it doesn't support managing remote computers.
So we need something in PowerShell that can run manage-bde on the remote machine - my choice would be Invoke-Command:
Get-Content 'C:\Users\Test\Test\IT\Lists\LaptopList.txt' | ForEach-Object {
Write-Host "Remoting into $_ to fetch BitLocker Drive Encryption status
Invoke-Command -ComputerName $_ -ScriptBlock { manage-bde -status }
}
Here, we instruct Invoke-Command to connect to the computer with whatever name is currently assigned to $_, and then execute manage-bde -status on the remote machine and return the resulting output, if any. Assuming that WinRM/PowerShellRemoting is configured on the remote machines, and the user executing this code locally is a domain account with local admin privileges on the remote computers, this will work as-is.
Further reading:
about_Remote
about_Remote_Requirements
about_Remote_Troubleshooting
Firstly, I'm by no means a PS expert, total newbie - admission done. I have scoured the internet for what I need in order to get the script to do what I want, but I've reached a point where I'm struggling and in need of help.
Basically, I've created a script using ISE that grabs the users in an AD OU, processes them by disabling the accounts, renaming them, stripping out the groups and moving them to another folder. In order to automate the deactivation process for users. But I now need to create a log file every time this runs, to show a) if it found any Users in the original OU (ToBeProcessed) and b) what processes were run and if they were successful. Here is the code.
$OUToBeProcessed = "OU=ToBeProcessed,OU=Users,OU=World,DC=local"
$OURetired = "OU=RetiredUsers,OU=Users,OU=World,DC=local"
$Users = Get-ADUser -SearchBase $OUToBeProcessed -Filter 'name -Like "*"' -Properties MemberOf
ForEach($User in $Users){
$SAN = $User.SamAccountName
#Disable user account
Disable-ADAccount -Identity $SAN
#Remove membership from groups for user
$User.Memberof | Remove-ADGroupMember -Member $User -Confirm:$False
$NewDN = "zzz_" + $User.Name
#Change display name
set-aduser $User -Displayname $newDN -ErrorAction SilentlyContinue
#Change distinguished name
Get-ADUser $SAN | Rename-ADObject -Newname $NewDN
Write-Host "$SAN may already exist."
#Move account to RetiredUsers
Get-Aduser $SAN | Move-ADObject -TargetPath $OURetired
}
I'm assuming I'll need to either use a Write-Output or Log-File cmdlet, though someone had also suggested Transcript, but I don't think that's what I need.
I've tried a number of ways to incorporate the Write-Output into the script, it runs without errors, but no text file is produced. But I'm placing it within the loop which may be the issue. I've placed it outside the loop but I think because it's not being passed anything it's creating the file with nothing in it. Would really appreciate some help as to where the Write-Output might need to go if that is the right cmdlet.
Personally I tend to add a Log function to my scripts. Something like this (where I output to the host and file):
Function Log {
Param (
[Parameter(Mandatory=$true)] [string] $String,
[Parameter(Mandatory=$true)] [string] $LogFilePath,
[Parameter(Mandatory=$false)][ValidateSet("ERROR","WARN","INFO","DEBUG")] [string] $Level = "INFO"
)
$LogString = ((Get-Date -Format "s") +" $Level $env:USERNAME $String")
Write-Host $LogString
Out-File -Append -FilePath $LogFilePath -InputObject $LogString
}
Then you could do logging:
Log "Something wrong!" "c:\mylog.log" "WARN"
Log "Updated stuff" "c:\mylog.log"
Or search the http://www.powershellgallery.com/ for logging modules.
Example (haven't tried this one myself):
https://www.powershellgallery.com/packages/PSLogging/2.5.2
This seems like a very easy thing to do; however, I don't know anything about WMI. How would I go about determining if a directory is shared over the network on one of my servers (using PowerShell)? Also, is there a quick primer that I can read to get aquainted to WMI? I really want to be able to use it when scripting, but most resources don't show any examples with PowerShell.
Currently, I'm running a check to see if the "Server" service is running; and another check to see if a specific group has permissions on the directory.
$serversrvc = Get-Service -Name "Server"
$dirpath = "C:\My\Path\"
$pathacl = (Get-Acl -path $dirpath).Access
$group = "Domain\User"
# Test if the "Server" service is running
If ($serversrvc.Status -eq "Running")
{
echo '"Server" service is running.'
}
Else
{
echo '"Server" is NOT running.'
}
# Test if the $dirpath exists
If (Test-Path $dirpath)
{
echo "$dirpath exists."
}
Else
{
echo "$dirpath does not exist."
}
# Test if $group has permssions on $dirpath
foreach ($id in $pathacl)
{
If ($id.IdentityReference -eq $group)
{
echo "$group has access to $dirpath"
}
}
The Win32_Share WMI class returns all instances of shared folders on a computer. This will give you all shared folders on the local machine:
Get-WmiObject -Class Win32_Share
Notice the Path property, you can use the Filter parameter (equivalent to the WQL WHERE clause) to get back shares witha certain local path but you'd also need to double each slash (slash is a :
Get-WmiObject -Class Win32_Share -ComputerName PC1 -Filter "Path='C:\\Windows'"
If you get back a result the folder is shared, otherwise it isn't. Notice the ComputerName parameter, you can execute the wmi command against remote computers.
To get back a Boolean result, true if the folder is shared and false otherwise, cast the command to bool:
[bool](Get-WmiObject -Class Win32_Share -ComputerName PC1 -Filter "Path='C:\\Windows'")
You can use Get-WmiObject Win32_Share to enumerate the shares on a computer. You could also use net share but then you'd have to parse the output and why bother with that when you have the WMI output. :-)