Guest User

Untitled

a guest
Dec 19th, 2018
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.62 KB | None | 0 0
  1. <#
  2. .SYNOPSIS
  3. The synopsis goes here. This can be one line, or many.
  4. This version of the template has inbuilt functions to capture credentials and store it securely for reuse
  5. Avoids the need to have plaintext passwords in the script
  6.  
  7. .DESCRIPTION
  8. The description is usually a longer, more detailed explanation of what the script or function does.
  9. Take as many lines as you need.
  10.  
  11. There are two main components of this script:
  12. 1. Prepare Credentials
  13. This phase is run at least once. It generates a secure hashed credential file that can be used by the script.
  14. Re-running this phase will overwrite the existing credential file.
  15. There are two modes provided:
  16. - DPAPI: Recommended. More secure. Requires both 'PrepareCredentials' and 'Execution' phases to be run by the same user account on the same machine to be able to decrypt key
  17. - AES: Only to be used when service account cannot be used to run in interactive mode (and thus can't run PrepareCredentials). The AES.Key file that is generated is decryptable by anyone, so you must protect read access to this file using ACLs.
  18.  
  19. 2. Execution
  20. This phase is used to actually perform the exection of this script. 'Prepare Credentials' must have been run at least once.
  21.  
  22. Author: David Lee (david.lee@kloud.com.au)
  23. Change Log:
  24. 07/12/15 0.1 DL Initial Release
  25.  
  26. .PARAMETER PrepareCredentials
  27. Run with this switch at least once. Two methods are provided for generating the secure hashed credential file based on your requirements.
  28.  
  29. .PARAMETER Execution
  30. Run this switch when you want to run this script normally
  31.  
  32. .PARAMETER param1
  33. These parameters are automatched against the parameters defined in this script
  34.  
  35. .PARAMETER param2
  36. Use a .parameter for each one of your parameters
  37.  
  38. .EXAMPLE
  39. .\script-name.ps1 -PrepareCredentials
  40.  
  41. .EXAMPLE
  42. .\script-name.ps1 -Execution -param1 value1 -param2 value2
  43.  
  44. #>
  45. [CmdletBinding()]
  46. Param([switch]$PrepareCredentials, [switch]$Execution, $param1, $param2)
  47.  
  48. <##====================================================================================
  49. GLOBAL CONFIGURATION
  50. ##===================================================================================#>
  51. $erroractionpreference = "stop"
  52.  
  53. $debugFlag = $PSBoundParameters.Debug.IsPresent
  54. $verboseFlag = $PSBoundParameters.Verbose.IsPresent
  55.  
  56. # Use this to control whether to break on debugs or not
  57. if ($debugFlag -eq $true) {
  58. # If you want a break prompt on each Write-Debug entry, use "inquire"
  59. # Otherwise use "continue" to simply output debug log (recommended)
  60. $debugPreference = "continue"
  61. }
  62.  
  63. # This will be used in file paths below, so avoid using spaces and special characters
  64. $scriptName = "Script_Name"
  65.  
  66. # Root Folder
  67. $rootFolder = "C:\Scripts\$scriptName"
  68.  
  69. # Secure Credential File
  70. $credentialFileDir = $rootFolder
  71. $credentialFilePath = "$credentialFileDir\$scriptName-SecureStore.txt"
  72.  
  73. # Path to store AES File (if using AES mode for PrepareCredentials)
  74. $AESKeyFileDir = $rootFolder
  75. $AESKeyFilePath = "$AESKeyFileDir\$scriptName-AES.key"
  76.  
  77. # Output Log File Name: DescriptiveName_yyyy-MM-ddTHHmm.log
  78. $outputLogFileName = "$scriptName_" + (get-date -f yyyy-MM-ddTHHmm) + ".log"
  79.  
  80. # Change Log File Directory as necessary
  81. $outputLogDir = "$rootFolder\Logs"
  82. # Full Path of Log File
  83. $outputLogPath = "$outputLogDir\$outputLogFileName"
  84.  
  85. # Log Age Limit (in days). Log files older than this will be auto deleted
  86. $logAgeLimit = 30
  87.  
  88. # This is where I put global variables and the like for easy access an updates
  89.  
  90. <##====================================================================================
  91. FUNCTIONS
  92. ##===================================================================================#>
  93.  
  94. <#
  95. .SYNOPSIS
  96. Initializes the output log file.
  97.  
  98. .DESCRIPTION
  99. Execute this function at the beginning of your main code to ensure the log file path
  100. exists, and if it doesn't, create the folder paths to ensure future Write-Log calls
  101. will run without issue
  102.  
  103. This function can also be modified to create log file headers
  104.  
  105. .PARAMETER overwrite
  106. Set this flag to overwrite any existing log files. Otherwise default is to append
  107. to the existing log file
  108.  
  109. .PARAMETER headerText
  110. Optional ability to define some text at the start of eveyr initialization of the log file
  111. Useful if you are not overwriting the log file each time script is run]
  112. #>
  113. function Start-LogFile([switch]$overwrite, $headerText) {
  114. # Check if log dir exists, if not, create it
  115. if (!(Test-Path $outputLogDir)) {
  116. New-Item -type Directory $outputLogDir | out-null
  117. }
  118. # Write-Output $headerText
  119.  
  120. try {
  121. if ($overwrite -eq $true) {
  122. set-content $outputLogPath $headerText
  123. }
  124. else {
  125. add-content $outputLogPath $headerText
  126. }
  127. }
  128. catch {
  129. Write-Warning "Could not initialize the log file: $outputLogPath"
  130. }
  131. }
  132.  
  133. <#
  134. .SYNOPSIS
  135. Writes to an entry into the output log file, and to the console if -Verbose is used
  136.  
  137. .DESCRIPTION
  138. This is a useful function for log file outputs. Change the structure of this output
  139. as necessary for your scripts.
  140. If the -Verbose flag is set, then the log file message will also be written to the console
  141. using the Write-Verbose command
  142.  
  143. .PARAMETER type
  144. Optional, but can use to tag the log entry type. Recommend to use one of INFO,WARNING,ERROR
  145. Will default to INFO if non specified.
  146. NOTE: If you wish to write a DEBUG log entry, use the Write-DebugLog function
  147.  
  148. .PARAMETER message
  149. The text to write to your ouput file
  150. #>
  151. function Write-Log($message, $type) {
  152. if ($type -eq $null -or $type -eq "") {
  153. $type = "INFO"
  154. }
  155.  
  156. try {
  157. # Log Entry Structure: [Date] [TYPE] [MESSAGE]
  158. $logEntry = (Get-Date -format u) + "`t" + $type.ToUpper() + "`t" + $message
  159. if ($type -eq "WARNING") {
  160. Write-Host -foregroundcolor Yellow $logEntry
  161. }
  162. elseif ($type -eq "ERROR") {
  163. Write-Host -foregroundcolor Red $logEntry
  164. }
  165. else {
  166. Write-Host $logEntry
  167. }
  168. Add-Content $outputLogPath $logEntry
  169. }
  170. catch {
  171. Write-Warning "Could not write entry to output log file: $outputLogPath `nLog Entry:$message"
  172. }
  173. }
  174.  
  175. <#
  176. .SYNOPSIS
  177. Writes to an entry ino the output log file only if the -debug parameter was set in the script
  178.  
  179. .DESCRIPTION
  180. This is a useful function for performing debug logging. It will use both the in built Write-Debug
  181. function as well as creating an entry to the log file with a DEBUG type
  182.  
  183. .PARAMETER message
  184. The debug text to write to your ouput file
  185. #>
  186. function Write-DebugLog($message) {
  187. Write-Debug $message
  188. try {
  189. # Only write to the log file if the -Debug parameter has been set
  190. if ($script:debugFlag -eq $true) {
  191. # Log Entry Structure: [Date] [TYPE] [MESSAGE]
  192. $logEntry = (Get-Date -format u) + "`t" + "DEBUG" + "`t" + $message
  193. Add-Content $outputLogPath $logEntry
  194. }
  195. }
  196. catch {
  197. Write-Warning "Could not write entry to output log file: $outputLogPath `nLog Entry:$message"
  198. }
  199. }
  200.  
  201. <#
  202. .SYNOPSIS
  203. Cleans up log files
  204.  
  205. .DESCRIPTION
  206. This is a useful function when scripts are run regularly and thus create lots of log files.
  207. Based on a log age date, it will remove all log files older than that period
  208. Uses a Global variable for the log age date
  209.  
  210. .PARAMETER logPath
  211. Folder location for log files to remove
  212.  
  213. .PARAMETER fileExtension
  214. Type of files to delete. Use a wildcard format like "*.log"
  215. #>
  216. function Cleanup-LogFiles($logPath, $fileExtension) {
  217. # Determine the date of which files older than specific period will be deleted
  218. $dateToDelete = (Get-Date).AddDays(-$logAgeLimit)
  219. $filesToDelete = Get-ChildItem $logPath -Include $fileExtension -Recurse | where {$_.LastWriteTime -le $dateToDelete}
  220. foreach ($file in $filesToDelete) {
  221. $filePath = $file.FullName
  222. try {
  223. Write-DebugLog "Deleting $file..."
  224. Remove-Item $filePath -force | out-null
  225. }
  226. catch {
  227. Write-Log "Failed to delete old log file ($filePath)" -type WARNING
  228. }
  229. }
  230. }
  231.  
  232. <##====================================================================================
  233. MAIN CODE
  234. ##===================================================================================#>
  235.  
  236. # Checks to make sure at least either PrepareCredentials or Execution switches are run and not both.
  237. if (($PrepareCredentials -eq $false -and $Execution -eq $false) -or ($PrepareCredentials -eq $true -and $Execution -eq $true)) {
  238. Write-Host -foreground red "[ERROR] You must specify either -PrepareCredentials or -Execution"
  239. exit -1
  240. }
  241.  
  242. # Leave this code as is. This is the code to generate the secure files.
  243. if ($PrepareCredentials -eq $true) {
  244. try {
  245.  
  246. $headerText = (Get-Date -format u) + "`t" + "INIT" + "`t" + "****$scriptName log initialised in PrepareCredentials mode.****"
  247. Start-LogFile -headerText $headerText # Initialize the log file with a header
  248.  
  249. # Provide two
  250. $title = "Prepare Credentials Encryption Method"
  251. $message = "Which mode do you wish to use?"
  252.  
  253. $DPAPI = New-Object System.Management.Automation.Host.ChoiceDescription "&DPAPI", `
  254. "Use Windows Data Protection API. This uses your current user context and machine to create the encryption key."
  255.  
  256. $AES = New-Object System.Management.Automation.Host.ChoiceDescription "&AES", `
  257. "Use a randomly generated SecureKey for AES. This will generate an AES.key file that you need to protect as it contains the encryption key."
  258.  
  259. $options = [System.Management.Automation.Host.ChoiceDescription[]]($DPAPI, $AES)
  260.  
  261. $choice = $host.ui.PromptForChoice($title, $message, $options, 0)
  262.  
  263. switch ($choice) {
  264. 0 {$encryptMode = "DPAPI"}
  265. 1 {$encryptMode = "AES"}
  266. }
  267. Write-DebugLog "Encryption mode $encryptMode was selected to prepare the credentials."
  268.  
  269. Write-Log "Collecting Credentials to create a secure credential file..." -Type INFO
  270. # Collect the credentials to be used.
  271. $creds = Get-Credential
  272.  
  273. # Store the details in a hashed format
  274. $userName = $creds.UserName
  275. $passwordSecureString = $creds.Password
  276.  
  277. if ($encryptMode -eq "DPAPI") {
  278. $password = $passwordSecureString | ConvertFrom-SecureString
  279. }
  280. elseif ($encryptMode -eq "AES") {
  281. # Generate a random AES Encryption Key.
  282. $AESKey = New-Object Byte[] 32
  283. [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)
  284.  
  285. # Store the AESKey into a file. This file should be protected! (e.g. ACL on the file to allow only select people to read)
  286.  
  287. # Check if Credential File dir exists, if not, create it
  288. if (!(Test-Path $AESKeyFileDir)) {
  289. New-Item -type Directory $AESKeyFileDir | out-null
  290. }
  291. Set-Content $AESKeyFilePath $AESKey # Any existing AES Key file will be overwritten
  292. $password = $passwordSecureString | ConvertFrom-SecureString -Key $AESKey
  293. }
  294. else {
  295. # Placeholder in case there are other EncryptModes
  296. }
  297.  
  298.  
  299. # Check if Credential File dir exists, if not, create it
  300. if (!(Test-Path $credentialFileDir)) {
  301. New-Item -type Directory $credentialFileDir | out-null
  302. }
  303.  
  304. # Contents in this file can only be read and decrypted if you have the encryption key
  305. # If using DPAPI mode, then this can only be ready by the user that ran this script on the same machine
  306. # If using AES mode, then the AES.key file contains the encryption key
  307.  
  308. Set-Content $credentialFilePath $userName # Any existing credential file will be overwritten
  309. Add-Content $credentialFilePath $password
  310.  
  311. if ($encryptMode -eq "AES") {
  312. Write-Host -foreground Yellow "IMPORTANT! Make sure you restrict read access, via ACLs, to the AES.Key file that has been generated to ensure stored credentials are secure."
  313. }
  314.  
  315. Write-Log "Credentials collected and stored." -Type INFO
  316. Write-Host -foreground Green "Credentials collected and stored."
  317. }
  318. catch {
  319. $errText = $error[0]
  320. Write-Log "Failed to Prepare Credentials. Error Message was: $errText" -type ERROR
  321. Write-Host -foreground red "Failed to Prepare Credentials. Please check Log File."
  322. Exit -1
  323. }
  324. }
  325.  
  326.  
  327. if ($Execution -eq $true) {
  328.  
  329. $headerText = (Get-Date -format u) + "`t" + "INIT" + "`t" + "****$scriptName log initialised in Execution mode****"
  330. Start-LogFile -headerText $headerText # Initialize the log file with a header
  331.  
  332. # Check to ensure we have a secure credential file (i.e. -PrepareCredentials has been run) and that the contents are valid
  333. if (!(Test-Path $credentialFilePath)) {
  334. Write-Log "Could not find a secure credential file at $credentialFilePath. Exiting." -Type ERROR
  335. Write-Host -foreground red "[ERROR] Could not find a secure credential file at $credentialFilePath. Ensure that you have run the -PrepareCredentials parameter at least once for this script."
  336. exit -1
  337. }
  338.  
  339. # Check to see if we have an AES Key file. If so, then we will use it to decrypt the secure credential file
  340. if (Test-Path $AESKeyFilePath) {
  341. try {
  342. Write-DebugLog "Found an AES Key File. Using this to decrypt the secure credential file."
  343. $decryptMode = "AES"
  344. $AESKey = Get-Content $AESKeyFilePath
  345. }
  346. catch {
  347. $errText = $error[0]
  348. Write-Log "AES Key file detected, but could not be read. Error Message was: $errText" -type ERROR
  349. exit -1
  350. }
  351. }
  352. else {
  353. Write-DebugLog "No AES Key File found. Using DPAPI method, which requires same user and machine to decrypt the secure credential file."
  354. $decryptMode = "DPAPI"
  355. }
  356.  
  357. try {
  358. Write-DebugLog "Reading secure credential file at $credentialFilePath."
  359. $credFiles = Get-Content $credentialFilePath
  360. $userName = $credFiles[0]
  361. if ($decryptMode -eq "DPAPI") {
  362. $password = $credFiles[1] | ConvertTo-SecureString
  363. }
  364. elseif ($decryptMode -eq "AES") {
  365. $password = $credFiles[1] | ConvertTo-SecureString -Key $AESKey
  366. }
  367. else {
  368. # Placeholder in case there are other decrypt modes
  369. }
  370.  
  371. Write-DebugLog "Creating credential object..."
  372. $credObject = New-Object System.Management.Automation.PSCredential -ArgumentList $userName, $password
  373. $passwordClearText = $credObject.GetNetworkCredential().Password
  374. Write-DebugLog "Credential store read. UserName is $userName and Password is $passwordClearText"
  375.  
  376.  
  377.  
  378. #**** Put Main Code here. The credentials you have stored is available as $credObject ****
  379.  
  380.  
  381. # Example Log File Entry
  382. # Write-Log "No Type Flag creates an INFO entry"
  383. # Write-Log "Or you can create a WARNING entry" -type "WARNING"
  384. # Write-Log "Or an ERROR entry" -type "ERROR"
  385.  
  386. # Example Debug Log
  387. Write-DebugLog "This writes stuff if the -Debug flag is set in the script"
  388.  
  389. return $passwordClearText
  390.  
  391.  
  392. }
  393. catch {
  394. $errText = $error[0]
  395. Write-Log "Could not execute in Execution mode. Error Message was: $errText" -type ERROR
  396. exit -1
  397. }
  398.  
  399. }
  400.  
  401. <##====================================================================================
  402. END OF CODE
  403. ##===================================================================================#>
Add Comment
Please, Sign In to add comment