Powershell multiple credential setup - windows

I just setup a powershell to collect disk space info among a group of servers.
but I encounter an issue that while I try to authenticate from one server to another, it require different credential info.
E.g.
SQLServer01
ID: domain1\sqladmin1
PW: 123456
SQLServer02
ID: domain2\sqladmin2
PW: 654321
right now i manage to setup first one with limited power-shell experience.
$comp= Get-Content "C:\disk_info\Computers.txt"
$diskvalue = #()
#$cre = get-Credential
$username = "domain1\sqladmin1"
$password = "123456"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList $user, $secureStringPwd
foreach($pc in $comp)
{
$diskvalue += Get-WmiObject -Class Win32_logicaldisk -ComputerName $pc -credential $creds -Filter DriveType=3 |
Select SystemName , DeviceID , #{Name=”size(GB)”;Expression={“{0:N1}” -f($_.size/1gb)}}, #{Name=”freespace(GB)”;Expression={“{0:N1}” -f($_.freespace/1gb)}}, #{Name=”UsedSpace(GB)”;Expression={“{0:N2}” -f(($_.size - $_.FreeSpace)/1gb)}}
#$diskvalue -replace ":",""
$diskvalue | Export-Csv C:\disk_info\DiskReport.csv -NoTypeInformation
}
Yet I am trying to input another credential for some servers only. domain2\ in this case.
Computer.txt as reference
sqlserver1.domain1.com
sqlserver2.domain1.domain2.com
sqlserver3.domain1.com
the one including "domain2" would be the need of multiple credential.

If you are using the full qualified domain names for the machine in Computers.txt, you could use a simple if statement to decide which domain credentials to use. You will just need to change the $domain2Match variable at the top to your 2nd domain in the below script ($domain2Match='.domain1.domain2.com').
$comp= Get-Content "C:\disk_info\Computers.txt"
$diskvalue = #()
# Put your FQDN without the server name here for the seconded domain
$domain2Match = '.domain1.domain2.com'
# Credential 1
$username = "domain1\sqladmin1"
$password = "123456"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList $user, $secureStringPwd
# Credential 2
$username2 = "domain2\sqladmin2"
$password2 = "123456"
$secureStringPwd2 = $password2 | ConvertTo-SecureString -AsPlainText -Force
$creds2 = New-Object System.Management.Automation.PSCredential -ArgumentList $user2, $secureStringPwd2
foreach($pc in $comp)
{
$credsToUse = $null
If($pc -imatch $domain2Match){
# Matched use domain 2 Credential
$credsToUse = $creds2
}Else {
# No match use domain 1 Credential
$credsToUse = $creds
}
$diskvalue += Get-WmiObject -Class Win32_logicaldisk -ComputerName $pc -credential $credsToUse -Filter DriveType=3 |
Select SystemName , DeviceID , #{Name=”size(GB)”;Expression={“{0:N1}” -f($_.size/1gb)}}, #{Name=”freespace(GB)”;Expression={“{0:N1}” -f($_.freespace/1gb)}}, #{Name=”UsedSpace(GB)”;Expression={“{0:N2}” -f(($_.size - $_.FreeSpace)/1gb)}}
$diskvalue | Export-Csv C:\disk_info\DiskReport.csv -NoTypeInformation
}

You can create two Credenital Objects:
Cred 1
$username = "domain1\sqladmin1"
$password = "123456"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList $user, $secureStringPwd
Cred 2
$username = "domain2\sqladmin2"
$password = "123456"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$creds2 = New-Object System.Management.Automation.PSCredential -ArgumentList $user, $secureStringPwd
Then use an IF Statement along with splatting Params (will be easier and cleaner)
foreach($pc in $comp)
{
$Params = #{
Class = "Win32_logicaldisk"
ComputerName = $pc
Credential = $creds
Filter = 'DriveType=3'
}
If ($pc -eq "SQLServer02")
{
$Params["Credential"] = $creds2
}
$diskvalue += Get-WmiObject #Params | Select SystemName , DeviceID , #{Name=”size(GB)”;Expression={“{0:N1}” -f($_.size/1gb)}}, #{Name=”freespace(GB)”;Expression={“{0:N1}” -f($_.freespace/1gb)}}, #{Name=”UsedSpace(GB)”;Expression={“{0:N2}” -f(($_.size - $_.FreeSpace)/1gb)}}
#$diskvalue -replace ":",""
$diskvalue | Export-Csv C:\disk_info\DiskReport.csv -NoTypeInformation
}

Related

Automate user homedirectory creation Powershell

I'm automating the process of creating LocalUsers on Windows systems. So far I used the Microsoft docs on New-LocalUser which has worked fine to create the account, this is my code so far:
function New-AdminUser {
param(
[Parameter(Position=0)]
[string] $UNameLocal,
[Parameter(Position=1)]
[string] $UDescription,
[Parameter(Position=2)]
[System.Security.SecureString] $Password
)
New-LocalUser -Name $UNameLocal -Description $UDescription -Password $Password -AccountNeverExpires -Confirm
Add-LocalGroupMember -Group "Administrators" -Member $UNameLocal
}
But this command does not actually generate the homedirectory in C:\Users\username.
I can create this by manually logging into the created user, but I want to automate this in Powershell. I couldn't find anything in the LocalAccounts module.
Is there any way to automate local account setup in Windows 10 using Powershell, without having to manually log in to a new account?
If you start a process (cmd /c) as the created user, it will create his profile. Add this to your function:
$Cred = New-Object System.Management.Automation.PSCredential ("$UNameLocal", $Password)
Start-Process "cmd.exe" -Credential $Cred -ArgumentList "/C" -LoadUserProfile
Here is the code:
param([Parameter(Mandatory=$true)][String]$samAccountName)
$fullPath = "\\srv2012r2\Users\{0}" -f $samAccountName
$driveLetter = "Z:"
$User = Get-ADUser -Identity $samAccountName
if($User -ne $Null) {
Set-ADUser $User -HomeDrive $driveLetter -HomeDirectory $fullPath -ea Stop
$homeShare = New-Item -path $fullPath -ItemType Directory -force -ea Stop
$acl = Get-Acl $homeShare
$FileSystemRights = [System.Security.AccessControl.FileSystemRights]"Modify"
$AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow
$InheritanceFlags = [System.Security.AccessControl.InheritanceFlags]"ContainerInherit, ObjectInherit"
$PropagationFlags = [System.Security.AccessControl.PropagationFlags]"InheritOnly"
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule ($User.SID, $FileSystemRights, $InheritanceFlags, $PropagationFlags, $AccessControlType)
$acl.AddAccessRule($AccessRule)
Set-Acl -Path $homeShare -AclObject $acl -ea Stop
Write-Host ("HomeDirectory created at {0}" -f $fullPath)
}
and here is the reference:
https://activedirectoryfaq.com/2017/09/powershell-create-home-directory-grant-permissions/

How to Apply Encrypt/Decrypt in Powershell?

Im using in PS the next command:
"Password" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
This generate a Key that im saving as "Key.txt" file
Now i want to decrypt that password using this:
$password = Get-Content password.txt (or just copy-pasting the key)
$cred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $username,($password | ConvertTo-SecureString)
BUT...
how i supose to add that to this...
$EmailFrom = "MyMail#gmail.com"
$EmailTo = "MayMail#gmail.com"
$Subject = "Test"
$Body = "this is a Test"
$SMTPServer = "smtp.gmail.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("My_USer", "My_Password");
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
I want to add it as My_Password, of course i should add a variable $password that comes from the Key.txt file for example, but then...?
Nope, storing in plain text is not good at all, but if you are not concerned about that then it's there.
You have other options, with secure / encrypted files and Windows CredMan:
Quickly and securely storing your credentials – PowerShell
To get a credential object we can either manually create one or use the Get-Credential cmdlet to prompt for the account details:
$Credential = Get-Credential
To store the credentials into a .cred file:
$Credential | Export-CliXml -Path "${env:\userprofile}\Jaap.Cred"
And to load the credentials from the file and back into a variable:
$Credential = Import-CliXml -Path "${env:\userprofile}\Jaap.Cred"
Invoke-Command -Computername 'Server01' -Credential $Credential {whoami}
Securely Store Credentials on Disk
Allow multiple users to access credentials stored using export-clixml
How to run a PowerShell script against multiple Active Directory domains with different credentials
PowerShell Credentials Manager
CredMan.ps1 is a PowerShell script that provides access to the Win32 Credential Manager API used for management of stored credentials.
https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Credentials-d44c3cde
And modules to use
https://powershellgallery.com/packages/BetterCredentials
https://powershellgallery.com/packages/CredentialManager
https://powershellgallery.com/packages/IntelliTect.CredentialManager
First we save the credentials
"Password123" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File C:\key.txt -NoNewline
Then we can use it just like this :
$SMTPClient = New-Object Net.Mail.SmtpClient("SomeServer", 587)
$SMTPClient.Credentials = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist ThisIsAUserName ,($(Get-Content C:\key.txt) | ConvertTo-SecureString)
And we can check to make sure it loaded correctly like this :
$SMTPClient.Credentials | select username, password
The output looks like this
UserName Password
-------- --------
ThisIsAUserName Password123

Get Size, location and name of each shared folder

I have this code that generates a list of all the shares and the size however can not generate a txt with this information or the shared location
$servers = #("servername")
$sizes = #()
foreach($server in $servers) {
write-host "Server: $server"
(gwmi -class Win32_Share -ComputerName $server -filter "Type = 0" |
% {
write-host " share: $($_.Name)"
$s = gci \\$server\$($_.Name) -recurse -force | Measure-Object -Property length -Sum
New-Object PSObject -property #{Name=$_.Name; Server=$server; TotalSize=$s.Sum }
})
}
And this not only shows me the size and generates txt size and can generate txt
Get-WmiObject Win32_share -computer server01 | FT "server01", path, name > ServerShares.txt
Get-WmiObject Win32_share -computer server02 | FT "server02", path, name >> ServerShares.txt
Someone could help me to create only one that does everything
In your New-Object you just need to add additional properties to get the information you want:
If you're not running PowerShell v3, remove [Ordered]
$servers = #("servername")
$sizes = #()
foreach($server in $servers)
{
write-host "Server: $server"
# Get all shares
$shares = Get-WmiObject -class Win32_Share -ComputerName $server -filter "Type = 0"
# go through each share
foreach($share in $shares)
{
write-host " share: $($share.Name)"
# Get size of share
$size = Get-ChildItem -Path "\\$server\$($_.Name)" -recurse -force | Measure-Object -Property length -Sum
# Create a new object to store information
New-Object PSObject -property ([ordered]#{
# Name of share
Name = $share.Name
# Share path
Path = $share.path
# What server share is on
Server = $server
# Total size of share
TotalSize = $size.Sum
# Change this path to where you want the file to be saved to
}) | Export-Csv -Path C:\ShareDetails.csv -NoTypeInformation -Append
}
}
I made a small revision to #Bluecakes response in order to use COM instead of .NET to capture the size information. This overcomes the path-length issues.
# Get size of share
# $size = Get-ChildItem -Path "$($share.Name)" -recurse -force | Measure-Object -Property length -Sum
$objFSO = New-Object -com Scripting.FileSystemObject
$size = "{0:N2}" -f (($objFSO.GetFolder("$($share.Name)").Size) / 1MB)
Then you also need to remove ".sum"
# Total size of share
TotalSize = $size

Create network share on machine outside domain

Issue:
Create a network share on a remote machine (Win 2008 Server) from a .ps1 script and provide username and password.
This works fine where the current user has enough privileges
$share = Get-WmiObject Win32_Share -List -ComputerName 10.0.0.1
$share.create("C:\dir","foo", 0)
When targetServer is a machine where my user does not have enough privileges I need a way to provide the username and password
Get-WmiObject has a parameter -Credential, so you can use Get-Credential to prompt the user for credentials and pass them like this:
$cred = Get-Credential
$share = Get-WmiObject Win32_Share -List -Computer 10.0.0.1 -Credential $cred
$share.Create('C:\dir', 'foo', 0)
If you want to fully automate credential handling, you can build a credential object yourself:
$username = 'DOMAIN\user'
$password = 'password'
$pw = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object Management.Automation.PSCredential $username, $pw
$share = Get-WmiObject Win32_Share -List -Computer 10.0.0.1 -Credential $cred
$share.Create('C:\dir', 'foo', 0)
Storing plaintext passwords in a script is not a good practice, though, so you may want to store the encrypted password in a separate file (make sure to set restrictive permissions on that file):
PS C:\> Read-Host 'Enter password' -AsSecureString |
>> ConvertFrom-SecureString | Out-File 'C:\password.txt'
>>
Enter password: ******
PS C:\> Get-Content 'C:\password.txt'
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000a04a09e406c93741865ab010ed072699
0000000002000000000003660000c00000001000000028b14f21c501cdf629b94cea2837e8520000
000004800000a0000000100000002db774b0a8612917224e7dbfd69055340800000043f449c82f47
3e78140000007de59fcec57a1dc9b6b62272eff517b8bf5291e5
The encrypted password can then be read and decrypted like this:
$username = 'DOMAIN\user'
$pw = Get-Content 'C:\password.txt' | ConvertTo-SecureString
$cred = New-Object Management.Automation.PSCredential $username, $pw
$share = Get-WmiObject Win32_Share -List -Computer 10.0.0.1 -Credential $cred
$share.Create('C:\dir', 'foo', 0)
Note, however, that ConvertTo-SecureString encrypts/decrypts the password with the user (and I think the host) running the command. If you need to encrypt the password as one user and decrypt it as another user (or on another host) you must share a key between those users/hosts. A random key can be generated like this:
PS C:\> $numchars = 24 # 192 Bit
PS C:\> $charmap = [char]'A'..[char]'Z' +
>> [char]'a'..[char]'z' +
>> [char]'0'..[char]'9'
>>
PS C:\> [char[]]($charmap | sort {Get-Random})[1..$numchars] -join ''
b69cGXKpJaI5tLrj4lHEPN3e
Create the password file with a shared key like this:
PS C:\> $key = [char[]]($charmap | sort {Get-Random})[1..$numchars] -join ''
PS C:\> $pw = Read-Host 'Enter password' -AsSecureString |
>> ConvertFrom-SecureString -Key ([byte[]][char[]]$key)
>>
Enter password: ******
PS C:\> #"
>> Key = $key
>> Pass = $pw
>> "# | Out-File 'C:\password.txt'
>>
and read the encrypted password like this:
$username = 'DOMAIN\user'
$pass = Get-Content 'C:\password.txt' | Out-String | ConvertFrom-StringData
$pw = ConvertTo-SecureString $pass.Pass -Key ([byte[]][char[]]$pass.Key)
$cred = New-Object Management.Automation.PSCredential $username, $pw
$share = Get-WmiObject Win32_Share -List -Computer 10.0.0.1 -Credential $cred
$share.Create('C:\dir', 'foo', 0)

PowerShell v1 Script Prompting for Password

I am trying to add remote server authentication to a PS1 batch file script.
So I can do this:
Copy-Item $src $destination -Credential $Creds
I created a password file that for now is in the same directory as the script. It simply contains the password string.
The line that causes the prompt:
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File password.txt
When I remove the Read-Host command, the prompt goes away and the script executes as expected.
Question
What's the correct way to do remote server authentication?
Here is the new code in context of the script:
[...]
if(-not(Test-Path $destination)){mkdir $destination | out-null}
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File password.txt
$PW = Get-Content password.txt | ConvertTo-Securestring
$Creds = New-Object -Typename System.Management.Automation.PSCredential -Argumentlist "SERVER02\Administrator",$PW
ForEach ($sourcefile In $(Get-ChildItem $source | Where-Object { $_.Name -match "Daily_Reviews\[\d{1,12}-\d{1,12}\].journal" }))
{
[...]
Copy-Item $src $destination -Credential $Creds
[...]
}
If you aren't worried about portability of the password file between machines, you can use this fairly secure approach:
# Capture once and store to file - DON'T PUT THIS PART IN YOUR SCRIPT
$passwd = Read-Host "Enter password" -AsSecureString
$encpwd = ConvertFrom-SecureString $passwd
$encpwd
$encpwd > $path\password.bin
# Later pull this in and restore to a secure string
$encpwd = Get-Content $path\password.bin
$passwd = ConvertTo-SecureString $encpwd
$cred = new-object System.Management.Automation.PSCredential 'john',$passwd
$cred
# NOTE: The "secret" required to rehyrdate correctly is stored in DPAPI - consequence:
# You can only rehydrate on the same machine that did the ConvertFrom-SecureString
If you need to debug this to see if $passwd is correct you can execute this while debugging:
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($passwd)
$str = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
$str

Resources