######################################################## ## ## AdminPWChange.ps1 ## ## Author: Thomas Kurth/Netree ## Date: 4.8.2014 ## ## History ## 001: First Version ## 002: Asymetric Encryption ## ######################################################## ## Manual Variable Definition ######################################################## $Debug = $false $ErrorActionPreference = "Stop" $publickey = "xFrv7zv0DsyZSXh+kAeYuKWc5qI1q5V36iV96lk4It+5RnET0JiJ30J3fvEKJKlwm+1D40SqlcMTz+RPwklygnoa4oa+DcZp0fX2hILefdMiSlPMWrxvOw5rkWAqvDZSswOwUeL2nr5feh2TYHKrivx8uckXSO5gwxWbIO6Sgw8=AQAB" $pwlength = 24 # Length of the random admin password $serverfqdn = "SERVERNAME_FQDN" # Servername where the netECM:MiniWebService is installed $LogFilePath = "C:\Windows\Logs\SCCM\AdminPWChange.log" $ScriptName = "AdminPWChange.ps1" $LogFilePathScriptPath = "C:\Program Files\Netree\netECMLauncher" # This is only used if the filename could not be resolved(IE running in ISE) ## Functions ######################################################## #Write text to a logfile with the current time. function WriteLog { param( [Parameter(Mandatory=$True,Position=1,ValueFromPipeline=$true, HelpMessage='Text to write to log file.')] [string]$Text ) Out-file -FilePath $LogFilePath -force -append -InputObject ((Get-Date –f o) + " " + $Text) # Disabled, Host Output is needed for Compliance Item Write-Host ((Get-Date –f o) + " " + $Text) } ################# # Powershell Allows The Loading of .NET Assemblies # Load the Security assembly to use with this script ################# [Reflection.Assembly]::LoadWithPartialName("System.Security") > $null ################# # This function is to Encrypt A String. # $string is the string to encrypt # $publickey can be generated with # $RSAPROV = New-Object -TypeName 'System.Security.Cryptography.RSACryptoServiceProvider' # $RSAPROV.ToXmlString($false) ################# function Encrypt-String($String, $publickey) { # Load the RSA Crypto Service Provider $RSAPROV = New-Object -TypeName 'System.Security.Cryptography.RSACryptoServiceProvider' $RSAPROV.FromXmlString($publickey) # Transform String to UTF8 Byte array $enc = [system.Text.Encoding]::UTF8 [byte[]]$data = $enc.GetBytes($String) #Encrypt data $Encrypted = $RSAPROV.Encrypt($data,$false) # Create Base64 String $EncryptedString = [System.Convert]::ToBase64String($Encrypted) return $EncryptedString } <# .Synopsis Generates one or more complex passwords designed to fulfill the requirements for Active Directory .DESCRIPTION Generates one or more complex passwords designed to fulfill the requirements for Active Directory .EXAMPLE New-SWRandomPassword Will generate one password with a length of 8 chars. .EXAMPLE New-SWRandomPassword -MinPasswordLength 8 -MaxPasswordLength 12 -Count 4 Will generate four passwords with a length of between 8 and 12 chars. .OUTPUTS [String] .NOTES Written by Simon Wåhlin, blog.simonw.se I take no responsibility for any issues caused by this script. .FUNCTIONALITY Generates random passwords .LINK http://blog.simonw.se/powershell-generating-random-password-for-active-directory/ #> function New-SWRandomPassword { [CmdletBinding(ConfirmImpact='Low')] [OutputType([String])] Param ( # Specifies minimum password length [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateScript({$_ -gt 0})] [Alias("Min")] [int]$MinPasswordLength = 8, # Specifies maximum password length [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=1)] [ValidateScript({$_ -ge $MinPasswordLength})] [Alias("Max")] [int]$MaxPasswordLength = 12, # Specifies an array of strings containing charactergroups from which the password will be generated. # At least one char from each group (string) will be used. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=2)] [String[]]$InputStrings = @('abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '0123456789', '!"#%&'), # Specifies number of passwords to generate. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=3)] [ValidateScript({$_ -gt 0})] [int]$Count = 1 ) Begin { Function Get-Seed{ # Generate a seed for future randomization $RandomBytes = New-Object -TypeName 'System.Byte[]' 4 $Random = New-Object -TypeName 'System.Security.Cryptography.RNGCryptoServiceProvider' $Random.GetBytes($RandomBytes) [BitConverter]::ToInt32($RandomBytes, 0) } } Process { For($iteration = 1;$iteration -le $Count; $iteration++){ # Create char arrays containing possible chars [char[][]]$CharGroups = $InputStrings # Set counter of used groups [int[]]$UsedGroups = for($i=0;$i -lt $CharGroups.Count;$i++){0} # Create new char-array to hold generated password if($MinPasswordLength -eq $MaxPasswordLength) { # If password length is set, use set length $password = New-Object -TypeName 'System.Char[]' $MinPasswordLength } else { # Otherwise randomize password length $password = New-Object -TypeName 'System.Char[]' (Get-Random -SetSeed $(Get-Seed) -Minimum $MinPasswordLength -Maximum $($MaxPasswordLength+1)) } for($i=0;$i -lt $password.Length;$i++){ if($i -ge ($password.Length - ($UsedGroups | Where-Object {$_ -eq 0}).Count)) { # Check if number of unused groups are equal of less than remaining chars # Select first unused CharGroup $CharGroupIndex = 0 while(($UsedGroups[$CharGroupIndex] -ne 0) -and ($CharGroupIndex -lt $CharGroups.Length)) { $CharGroupIndex++ } } else { #Select Random Group $CharGroupIndex = Get-Random -SetSeed $(Get-Seed) -Minimum 0 -Maximum $CharGroups.Length } # Set current position in password to random char from selected group using a random seed $password[$i] = Get-Random -SetSeed $(Get-Seed) -InputObject $CharGroups[$CharGroupIndex] # Update count of used groups. $UsedGroups[$CharGroupIndex] = $UsedGroups[$CharGroupIndex] + 1 } Write-Output -InputObject $($password -join '') } } } ## Initialization ######################################################## WriteLog "Start Script" ## Main Script ######################################################## try{ #Get unencrypted Password $UnEncryptedPassword = New-SWRandomPassword -MinPasswordLength $pwlength -MaxPasswordLength $pwlength -InputStrings @('abcdefghijkmnopqrstuvwxyz', 'ABCDEFGHJKLMNPQRSTUVWXYZ', '23456789', '!-_#%&') WriteLog "Random PW generated" # Encrypt Password $EncryptedPassword = Encrypt-String -String $UnEncryptedPassword -publickey $publickey # Get Computername lowercase $computername = [string]$env:computername $computername = $computername.toLower() if($computername -eq $null){ throw "Computername not found" } WriteLog "Computername is: $computername" # Get local Administrator Username by SID $administrator = Get-WmiObject Win32_Account -Filter "DOMAIN='$computername'" | Where-Object {$_.SID -like "S-1-5-*" -and $_.SID -like "*-500"} if($administrator -eq $null -or $administrator.Name -eq $null){ throw "Administrator not found" } WriteLog "Administrator is: $($administrator.Name)" #Check Connection to Webservice $uri = "http://$serverfqdn/netECMMiniWebService/TSClient.svc" $prx = New-WebServiceProxy -uri $uri if($prx -eq $null){ throw "No connection to Web Service" } WriteLog "Connected to Web Service" #Save encrypted Password to Webservice $prx.AddPropStoreVar($computername,"password",$EncryptedPassword) WriteLog "Encrypted Password saved" # Set local Admin Password $user = [adsi]"WinNT://$computername/$($administrator.Name),user" $user.setpassword($UnEncryptedPassword) WriteLog "Password saved for $($administrator.Name)" Write-Host "Compliant" } catch { WriteLog "Error: $($_.Exception.Message)" Write-Host "NonCompliant" exit 99001 } ## Finishing ######################################################## WriteLog "End Script"