How to replace multiple certificates in IIS - windows

I want to replace certificate for few websites hosted by IIS. I created Powershell script:
Import-Module Webadministration
$certname = "E:\cert.pfx"
$certpwd = "zaq12wsx"
$hostname = "test.com"
$pfxpass = $certpwd |ConvertTo-SecureString -AsPlainText -Force
$newCert = Import-PfxCertificate -FilePath $certname `
-CertStoreLocation "Cert:\LocalMachine\My" `
-password $pfxpass
# fetch the default web site:
$sites = Get-ChildItem -Path "IIS:\Sites" | where {( $_.Name -like "*$hostname" )}
foreach ($site in $sites)
{
if ($site.Bindings.Collection.protocol -eq 'https'){
$binding = $site.Bindings.Collection | `
where {( $_.protocol -eq 'https' -and $_.bindingInformation -eq '*:443:')}
$binding.AddSslCertificate($newCert.Thumbprint, "my")
}
}
But I receive error:
You cannot call a method on a null-valued expression.
At line:22 char:9
+ $binding.AddSslCertificate($newCert.Thumbprint, "my")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Is there an any option to replace cert for few websites with the same domain?

If you have several sites using the same certificate, you want to batch update the SSL certificates in these several sites via script, you can try the following method, I think it is easier to implement:
***Assign Thumbprints to Variables***
Import-Module Webadministration
$OldThumbprint = "insert thumbprint of the existing certificate here"
$NewThumbprint = "insert the thumbprint of the new certificate here"
Get-WebBinding | Where-Object { $_.certificateHash -eq $OldThumbprint} | ForEach-Object {
Write-Host "Replacing Cert For " $_
$_.RemoveSslCertificate()
$_.AddSslCertificate($NewThumbprint, 'My')
}
The result of my test was successful:
This method requires you to retrieve the thumbprints of the old and new certificates first. Then you need to copy the thumbprints of the old and new certificates and replace them in the script.
On how to retrieve the thumbprint of the certificate, you can follow the steps in this document.
On how to view certificates with the MMC snap-in, you can refer to this document.

Related

Folder audit Powershell script, nested loop

I am trying to get a script working to audit folder permissions on a Windows server, among other data, and export this data to a CSV file for analysis after a ransomware attack.
I ripped the script from a forum, but it did not run correctly as is. Below is a slightly modified version during my troubleshooting.
I am well versed in batch scripting, and have a decent understanding of loops and pipelining, but this Powershell script has me scratching my head.
It seems like the array is not making it to the nested loop.
I am testing in Windows 10 Pro 21H1, using Powershell version 5.1.19041.1320, build 10.0.19041.1320
##The script:
$ErrorActionPreference = "Continue"
$strComputer = $env:ComputerName
$colDrives = Get-PSDrive -PSProvider Filesystem
ForEach ($DriveLetter in $colDrives) {
$StartPath = "$DriveLetter`:\"
Get-ChildItem -LiteralPath $StartPath -Recurse | ?{ $_.PSIsContainer } |
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Item -
LiteralPath $FullPath}.Directoryinfo.GetAccessControl())}
Select #{N='Server Name';E={$strComputer}}
#{N='Full Path';E={$FullPath}}
#{N='Type';E={If($FullPath.PSIsContainer -eq $True) {'D'} Else {'F'}}}
#{N='Owner';E={$_.Owner}}
#{N='Trustee';E={$_.IdentityReference}}
#{N='Inherited';E={$_.IsInherited}}
#{N='Inheritance Flags';E={$_.InheritanceFlags}}
#{N='Ace Flags';E={$_.PropagationFlags}}
#{N='Ace Type';E={$_.AccessControlType}}
#{N='Access Masks';E={$_.FileSystemRights}}
Export-CSV -NoTypeInformation -Delimiter "|" -Path "$strComputer`_$DriveLetter.csv"
##The error I am getting:
You cannot call a method on a null-valued expression.
At C:\Users\user\Documents\fileaudit2.ps1:8 char:13
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $ ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidOperation: (:) [], RuntimeException
FullyQualifiedErrorId : InvokeMethodOnNull
##when I modify the nested loop as follows:
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Item -LiteralPath $FullPath}).Directoryinfo.GetAccessControl()}
##I get the error:
Get-Item : Cannot evaluate parameter 'LiteralPath' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without
input.
At C:\Users\user\Documents\fileaudit2.ps1:8 char:46
... Path = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Ite ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : MetadataError: (:) [Get-Item], ParameterBindingException
FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.PowerShell.Commands.GetItemCommand
##I'm just wholly struggling to understand what is not working in this loop.
You are mixing a lot of unneeded Get-Item calls in there.
I also would not use Get-PSDrive for this because I assume you don't want to get results for CD drives, USB devices etc in the report.
Try:
# this returns drives WITH a trailing backslash like C:\
$colDrives = ([System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Fixed' }).Name
# or use:
# this returns drives WITHOUT trailing backslash like C:
# $colDrives = (Get-CimInstance -ClassName win32_logicaldisk | Where-Object { $_.DriveType -eq 3 }).DeviceID
$result = foreach ($drive in $colDrives) {
Get-ChildItem -LiteralPath $drive -Directory -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
$path = $_.FullName
$acl = Get-Acl -Path $path
foreach ($access in $acl.Access) {
[PsCustomObject]#{
Server = $env:COMPUTERNAME
Drive = $drive[0] # just the first character of the drive
Directory = $path
Owner = $acl.Owner
Trustee = $access.IdentityReference
Inherited = $access.IsInherited
InheritanceFlags = $access.InheritanceFlags -join ', '
'Ace Flags' = $access.PropagationFlags -join ', '
'Ace Type' = $access.AccessControlType
'Access Masks' = $access.FileSystemRights -join ', '
}
}
}
}
# now you can save your result as CSV file for instance you can double-click to open in Excel:
$result | Export-Csv -Path 'X:\WhereEver\audit.csv' -NoTypeInformation -UseCulture
To do this on several remote machines, wrap it inside Invoke-Command
# set the credentials for admin access on the servers
$cred = Get-Credential 'Please enter your admin credentials'
# create an array of the servers you need to probe
$servers = 'Server01', 'Server02'
$result = Invoke-Command -ComputerName $servers -Credential $cred -ScriptBlock {
$colDrives = ([System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Fixed' }).Name
foreach ($drive in $colDrives) {
# code inside this loop unchanged as above
}
}
# remove the extra properties PowerShell added
$result = $result | Select-Object * -ExcludeProperty PS*, RunspaceId
# output to csv file
$result | Export-Csv -Path 'X:\WhereEver\audit.csv' -NoTypeInformation -UseCulture

Getting "NullReference Exception" error while creating new organizational unit using Get-ADOrganizationalUnit]

Below is section of a large script that configures range of tasks remotely on a Active Directory server.
The script asks the user to enter OU name, saves it in a variable and passes it to AD server via Invoke-Command and $Using scope to transfer variable value to remote host and process the request
$value = Read-Host -Prompt "Enter Unique Name"
Invoke-Command -Session $testsession -ScriptBlock {
$DDN = "DC=Test,DC=net"
$OUdn = "OU=MainOU,"+$DDN
$COU = $Using:value
$Cdn = "OU="+$COU
$CPath = $Cdn+","+$OUdn
While ($true) {
Write-Host "Checking existence of OU"
if (Get-ADOrganizationalUnit -Filter "distinguishedName -eq '$CPath'") {
Write-Host "$COU OU exists."
$COU = $Null
$Cdn = $Null
$Cpath = $Null
$COU = Read-Host -Prompt "Enter Unique Name"
$Cdn="OU="+$CustOU
$CPath=$Cdn+","+OUdn
}else {
Write-Host "$COU is new"
New-ADOrganizationalUnit $COU -path $OUdn
if (Get-ADOrganizationalUnit -Filter "distinguishedName -eq '$CPath'") {
write-host " $COU is created "
}
Break
}
}
}
It gives the desired result when it is run separately and creates OU with the name provided under "Main OU". However, when it is combined with the main script it throws exception error. Main script also prompts to enter some more info which are used in other sections successfully but just not working in this section.
Am I missing anything? Your suggestions and helps are appreciated.
Object reference not set to an instance of an object.
+ CategoryInfo : NotSpecified: (:) [Get-ADOrganizationalUnit], NullReferenceException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.NullReferenceException,Microsoft.ActiveDirectory.Management.Commands.GetADOrganizationalUnit
This will help you understand why you're getting the error and will also help you in future scripts:
try
{
New-ADOrganizationalUnit $COU -path $OUdn
}
catch
{
#(
"Failed to create New Organizational Unit:"
"$_"
"Value for `$COU was: $COU"
"Value for `$OUdn was: $OUdn"
) | Out-String | Write-Warning
break
}
For more info: about_Try_Catch_Finally
As a side note, I don't see $CustOU being previously defined and I believe this a typo $Cdn+","+OUdn.

New-ADUser unable to concatanate name and domainname

I'm trying to create userprincipalname as combination of username and hardcoded domainname
csv file:
name,displayname,givenname,surname
dhall,Don Hall,Don,Hall
Code:
Import-csv "c:\output.csv" | ForEach-Object {new-aduser -name $_.name -UserPrincipalName ("{0}#{1}" -f $_.name,"Dev.domain.Net") -DisplayName "$($_.givenname $_.surname)" -givenName $_.givenname -surname $_.surname -path "OU=Workspaces,DC=Dev,DC=domain,DC=Net" -AccountPassword (convertto-securestring passs -asplaintext -force) -Enabled [System.Convert]::toboolean($true)) -ChangePasswordAtLogon ([system.convert]::ToBoolean($true))}
And getting:
At line:1 char:159
+ ... -f $_.name,"Dev.domain.Net") -DisplayNa
+
Unexpected token '$_' in expression or stateme
+ CategoryInfo : ParserError: (:)
+ FullyQualifiedErrorId : UnexpectedToken
Tried also -UserPrincipalName ([string]::Concat($_.name,"#dev.domain.net"))
but same error
PS C:\> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 1944
Windows server 2016
Not sure what's causing that error (don't have an AD controller to test against at the moment so can't validate) but in cases where I'm going to need to reuse a property multiple times I tend to assign it to a temporary variable (makes it slightly easier to write the code and don't run into issues with the pipeline variable getting updated).
Import-CSV "c:\output.csv" | ForEach-Object {
$name = $_.name
$upn = "{0}#{1}" -f $name,"dev.domain.net"
New-ADUser -Name $name -UserPrincipalName $upn #...
}

Powershell script to FTP files to a remote windows server

I have been struggling to get a script to work that can FTP a file to a remote windows server and wanted to see if I could get some help. I searched through multpile pages on StackOverflow and Google and have been unsuccessful so far.Below is the code I have
Logic is as follows:
Pick up the oldest file with a pattern within a folder
Connect to FTP server
Copy the file to the remote server
Move the file from the in folder to an archive folder
Code seems to be failing where I try to FTP the file to the server - $webclient.UploadFile($uri,$latestfile)
Getting this exception:
Exception calling "UploadFile" with "2" argument(s): "An exception occurred during a WebClient request."
At C:\Downloads\powershell\testmove3.ps1:22 char:26
+ $webclient.UploadFile <<<< ($uri,$latestfile)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
$source = "C:\downloads\"
$destination = "C:\dest\"
Get-ChildItem -Path C:\downloads | Where-Object { $_.name -like "TEST.CSV-PlainText*.txt" }
$latestfile=gci -path $source | Where-Object { $_.name -like "TEST.CSV-PlainText*.txt"} | sort FirstWriteTime | select -last 1
"Oldest File $latestfile"
## Get ftp object
$ftp_client = New-Object System.Net.WebClient
$user="someuser"
$pass="somepass"
$ftp_address = "ftp://ftp.testserver.com"
## Make uploads
$uri = New-Object System.Uri($ftp+$item.Name)
"Item is $latestfile"
$webclient.UploadFile($uri,$latestfile)
"File uploaded to remote servr"
Move-Item $latestfile.FullName $destination
"File $latestfile moved"
Ok, was on it overnight and happy to report that I got a solution - yeeehaw !!
I have even added a logic to trap an exception and send out an email notification. Hope this helps anyone with their FTP issues using Powershell. At the end, it returns a success or failure code to the calling program.
#PowerShell.exe -File "C:\temp\FTP.ps1"
trap [Exception] {
$recipient = "recipient#yahoo.com"
$sender = "sender#yahoo.com"
$server = "test.mailserver.com"
$subject = "FTP Test"
$body = "Exception Title: " + $_.Exception.GetType().FullName + "`r`n`r`n" + "Exception Details: " + $_.Exception.Message
$msg = new-object System.Net.Mail.MailMessage $sender, $recipient, $subject, $body
$client = new-object System.Net.Mail.SmtpClient $server
$client.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
$client.Send($msg)
exit 1
}
$ftpuser = "testuser"
$ftppass = "testpass"
$ftpserver = "ftp://ftp.testserver.com/"
$file = "C:\temp\one.txt"
$filenewname = "one.txt"
$webclient = New-Object System.Net.WebClient
$ftp = $ftpserver+$filenewname
$uri = New-Object System.Uri($ftp)
#Error was happening because the method call was attempting to use the HttpProxy on the Server machine.
#If the proxy is not set to null explicitly in your code, then you will get error - "An exception occurred during a webclient request"
$webclient.Proxy = $NULL
$webclient.Credentials = New-Object System.Net.NetworkCredential($ftpuser,$ftppass)
"Uploading $filenewname in $ftpserver"
$webclient.UploadFile($uri,$file)
"Uploaded $filenewname in $ftpserver"
return 0
If this is going to be used in any live support environment, I like to suggest using a prompt for password:
$ftppass = Read-Host "Enter password" -AsSecureString

'Session' Parameter is null or empty in PowerShell script

I'm very new to PowerShell (forgive my ignorance) and am trying to install a program remotely on multiple computers on my domain. Currently, I'm just trying to get it to work on one computer. The script below was found online and adapted for my needs. Yesterday it worked, but today it's complaining about the session parameter.
I don't fully understand "sessions", but I have ensured on the client machine that the winrm service is running and I have invoked Enable-PSRemoting -force.
Here's the script:
$computers = Get-Content "c:\tmpPS\computers.txt"
$rs = Get-PSSession
Get-PSSession | Get-Member
######
## Functions
################
foreach ($comp in $computers)
{
Write-Host "should work with $comp"
}
PushMSI
RemoteConnect
InstallMSI
Function PushMSI {
Write-Host "------------------------------------------------"
Write-Host "This will copy the MSI file from localhost c:\tmpPS\"
write-Host "------------------------------------------------"
Write-Host ""
Write-Host ""
foreach ($comp in $computers)
{
Copy-Item -path "c:\tmpPS\clientInstall.msi" -Destination \\$comp\c$\tmpPS
}
}
Function RemoteConnect
{
Write-Host "------------------------------------------------"
Write-Host "This will establish a PSSession with all computers in c:\temp\computers.txt"
write-Host "------------------------------------------------"
Write-Host ""
Write-Host ""
Get-Content C:\tmpPS\computers.txt | New-PSSession -ThrottleLimit 50
}
Function InstallMSI
{
Write-Host "------------------------------------------------"
Write-Host "This will Install UPS Update on all computers with an Established PSSession"
write-Host "------------------------------------------------"
Write-Host "After the Install PSSessions will be removed"
Write-Host ""
Invoke-Command -Session $rs -ScriptBlock {invoke-item "c:\tmpPS\ClientInstall.msi"}
}
Get-PSSession | Remove-PSSession
And here's the output:
PS C:\Users\Me> C:\tmpPS\remoteInstall.ps1
Get-Member : No object has been specified to the get-member cmdlet.
At C:\tmpPS\remoteInstall.ps1:3 char:17
+ Get-PSSession | Get-Member
+ ~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException
+ FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
should work with eSignWin81.informa.local
------------------------------------------------
This will copy the MSI file from localhost c:\tmpPS\
------------------------------------------------
------------------------------------------------
This will establish a PSSession with all computers in c:\temp\computers.txt
------------------------------------------------
Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
6 Session6 eSignWin81.i... Opened Microsoft.PowerShell Available
------------------------------------------------
This will Install UPS Update on all computers with an Established PSSession
------------------------------------------------
After the Install PSSessions will be removed
Invoke-Command : Cannot validate argument on parameter 'Session'. The argument is null or empty. Supply an argument that is not null or empty and then try
the command again.
At C:\tmpPS\remoteInstall.ps1:49 char:25
+ Invoke-Command -Session $rs -ScriptBlock {invoke-item "c:\tmpPS\ClientInstall.ms ...
+ ~~~
+ CategoryInfo : InvalidData: (:) [Invoke-Command], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeCommandCommand
Try not to use Write-Host as it brakes the pipeline and kills puppy's.
I've tried to improve your script a little bit, so you understand the logic behind the structure better. It hasn't been tested, but it should do the trick.
# First parameters
[CmdletBinding()]
Param(
[ValidateScript({Test-Path $_ -PathType leaf})]
$ComputerList = "c:\tmpPS\computers.txt",
[ValidateScript({Test-Path $_ -PathType leaf})]
$MSI = "c:\tmpPS\clientInstall.msi"
)
# Then functions
Begin {
Function Copy-MSI {
foreach ($Com in $Computers) {
Copy-Item -path $MSI -Destination "\\$Com\c$\tmpPS"
}
}
Function Install-MSI {
foreach ($Com in $Computers) {
Enter-PSSession -ComputerName $Com
invoke-item "c:\tmpPS\ClientInstall.msi"
Exit-PSSession
}
}
}
# Then the actions
Process {
$Computers = Get-Content $ComputerList
Copy-MSI
Install-MSI
}
If you run this, you'll find the information you're looking for:
Get-Help Enter-PSSession

Resources