Guest User

Untitled

a guest
Sep 19th, 2019
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.27 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. {
  59. # If you want a break prompt on each Write-Debug entry, use "inquire"
  60. # Otherwise use "continue" to simply output debug log (recommended)
  61. $debugPreference = "continue"
  62. }
  63.  
  64. # This will be used in file paths below, so avoid using spaces and special characters
  65. $scriptName = "Script_Name"
  66.  
  67. # Root Folder
  68. $rootFolder = "C:\Scripts\$scriptName"
  69.  
  70. # Secure Credential File
  71. $credentialFileDir = $rootFolder
  72. $credentialFilePath = "$credentialFileDir\$scriptName-SecureStore.txt"
  73.  
  74. # Path to store AES File (if using AES mode for PrepareCredentials)
  75. $AESKeyFileDir = $rootFolder
  76. $AESKeyFilePath = "$AESKeyFileDir\$scriptName-AES.key"
  77.  
  78. # Output Log File Name: DescriptiveName_yyyy-MM-ddTHHmm.log
  79. $outputLogFileName = "$scriptName_" + (get-date -f yyyy-MM-ddTHHmm) + ".log"
  80.  
  81. # Change Log File Directory as necessary
  82. $outputLogDir = "$rootFolder\Logs"
  83. # Full Path of Log File
  84. $outputLogPath = "$outputLogDir\$outputLogFileName"
  85.  
  86. # Log Age Limit (in days). Log files older than this will be auto deleted
  87. $logAgeLimit = 30
  88.  
  89. # This is where I put global variables and the like for easy access an updates
  90.  
  91. <##====================================================================================
  92. FUNCTIONS
  93. ##===================================================================================#>
  94.  
  95. <#
  96. .SYNOPSIS
  97. Initializes the output log file.
  98.  
  99. .DESCRIPTION
  100. Execute this function at the beginning of your main code to ensure the log file path
  101. exists, and if it doesn't, create the folder paths to ensure future Write-Log calls
  102. will run without issue
  103.  
  104. This function can also be modified to create log file headers
  105.  
  106. .PARAMETER overwrite
  107. Set this flag to overwrite any existing log files. Otherwise default is to append
  108. to the existing log file
  109.  
  110. .PARAMETER headerText
  111. Optional ability to define some text at the start of eveyr initialization of the log file
  112. Useful if you are not overwriting the log file each time script is run]
  113. #>
  114. function Start-LogFile([switch]$overwrite, $headerText)
  115. {
  116. # Check if log dir exists, if not, create it
  117. if(!(Test-Path $outputLogDir))
  118. {
  119. New-Item -type Directory $outputLogDir | out-null
  120. }
  121. Write-Output $headerText
  122.  
  123. try
  124. {
  125. if($overwrite -eq $true)
  126. {
  127. set-content $outputLogPath $headerText
  128. }
  129. else
  130. {
  131. add-content $outputLogPath $headerText
  132. }
  133. }
  134. catch
  135. {
  136. Write-Warning "Could not initialize the log file: $outputLogPath"
  137. }
  138. }
  139.  
  140. <#
  141. .SYNOPSIS
  142. Writes to an entry into the output log file, and to the console if -Verbose is used
  143.  
  144. .DESCRIPTION
  145. This is a useful function for log file outputs. Change the structure of this output
  146. as necessary for your scripts.
  147. If the -Verbose flag is set, then the log file message will also be written to the console
  148. using the Write-Verbose command
  149.  
  150. .PARAMETER type
  151. Optional, but can use to tag the log entry type. Recommend to use one of INFO,WARNING,ERROR
  152. Will default to INFO if non specified.
  153. NOTE: If you wish to write a DEBUG log entry, use the Write-DebugLog function
  154.  
  155. .PARAMETER message
  156. The text to write to your ouput file
  157. #>
  158. function Write-Log($message, $type)
  159. {
  160. if($type -eq $null -or $type -eq "")
  161. {
  162. $type = "INFO"
  163. }
  164.  
  165. try
  166. {
  167. # Log Entry Structure: [Date] [TYPE] [MESSAGE]
  168. $logEntry = (Get-Date -format u) + "`t" + $type.ToUpper() + "`t" + $message
  169. if($type -eq "WARNING")
  170. {
  171. Write-Host -foregroundcolor Yellow $logEntry
  172. }
  173. elseif($type -eq "ERROR")
  174. {
  175. Write-Host -foregroundcolor Red $logEntry
  176. }
  177. else
  178. {
  179. Write-Host $logEntry
  180. }
  181. Add-Content $outputLogPath $logEntry
  182. }
  183. catch
  184. {
  185. Write-Warning "Could not write entry to output log file: $outputLogPath `nLog Entry:$message"
  186. }
  187. }
  188.  
  189. <#
  190. .SYNOPSIS
  191. Writes to an entry ino the output log file only if the -debug parameter was set in the script
  192.  
  193. .DESCRIPTION
  194. This is a useful function for performing debug logging. It will use both the in built Write-Debug
  195. function as well as creating an entry to the log file with a DEBUG type
  196.  
  197. .PARAMETER message
  198. The debug text to write to your ouput file
  199. #>
  200. function Write-DebugLog($message)
  201. {
  202. Write-Debug $message
  203. try
  204. {
  205. # Only write to the log file if the -Debug parameter has been set
  206. if($script:debugFlag -eq $true)
  207. {
  208. # Log Entry Structure: [Date] [TYPE] [MESSAGE]
  209. $logEntry = (Get-Date -format u) + "`t" + "DEBUG" + "`t" + $message
  210. Add-Content $outputLogPath $logEntry
  211. }
  212. }
  213. catch
  214. {
  215. Write-Warning "Could not write entry to output log file: $outputLogPath `nLog Entry:$message"
  216. }
  217. }
  218.  
  219. <#
  220. .SYNOPSIS
  221. Cleans up log files
  222.  
  223. .DESCRIPTION
  224. This is a useful function when scripts are run regularly and thus create lots of log files.
  225. Based on a log age date, it will remove all log files older than that period
  226. Uses a Global variable for the log age date
  227.  
  228. .PARAMETER logPath
  229. Folder location for log files to remove
  230.  
  231. .PARAMETER fileExtension
  232. Type of files to delete. Use a wildcard format like "*.log"
  233. #>
  234. function Cleanup-LogFiles($logPath, $fileExtension)
  235. {
  236. # Determine the date of which files older than specific period will be deleted
  237. $dateToDelete = (Get-Date).AddDays(-$logAgeLimit)
  238. $filesToDelete = Get-ChildItem $logPath -Include $fileExtension -Recurse | where{$_.LastWriteTime -le $dateToDelete}
  239. foreach($file in $filesToDelete)
  240. {
  241. $filePath = $file.FullName
  242. try
  243. {
  244. Write-DebugLog "Deleting $file..."
  245. Remove-Item $filePath -force | out-null
  246. }
  247. catch
  248. {
  249. Write-Log "Failed to delete old log file ($filePath)" -type WARNING
  250. }
  251. }
  252. }
  253.  
  254. <##====================================================================================
  255. MAIN CODE
  256. ##===================================================================================#>
  257.  
  258. # Checks to make sure at least either PrepareCredentials or Execution switches are run and not both.
  259. if(($PrepareCredentials -eq $false -and $Execution -eq $false) -or ($PrepareCredentials -eq $true -and $Execution -eq $true))
  260. {
  261. Write-Host -foreground red "[ERROR] You must specify either -PrepareCredentials or -Execution"
  262. exit -1
  263. }
  264.  
  265. # Leave this code as is. This is the code to generate the secure files.
  266. if($PrepareCredentials -eq $true)
  267. {
  268. try
  269. {
  270.  
  271. $headerText = (Get-Date -format u) + "`t" + "INIT" + "`t" + "****$scriptName log initialised in PrepareCredentials mode.****"
  272. Start-LogFile -headerText $headerText # Initialize the log file with a header
  273.  
  274. # Provide two
  275. $title = "Prepare Credentials Encryption Method"
  276. $message = "Which mode do you wish to use?"
  277.  
  278. $DPAPI = New-Object System.Management.Automation.Host.ChoiceDescription "&DPAPI", `
  279. "Use Windows Data Protection API. This uses your current user context and machine to create the encryption key."
  280.  
  281. $AES = New-Object System.Management.Automation.Host.ChoiceDescription "&AES", `
  282. "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."
  283.  
  284. $options = [System.Management.Automation.Host.ChoiceDescription[]]($DPAPI, $AES)
  285.  
  286. $choice = $host.ui.PromptForChoice($title, $message, $options, 0)
  287.  
  288. switch ($choice)
  289. {
  290. 0 {$encryptMode = "DPAPI"}
  291. 1 {$encryptMode = "AES"}
  292. }
  293. Write-DebugLog "Encryption mode $encryptMode was selected to prepare the credentials."
  294.  
  295. Write-Log "Collecting Credentials to create a secure credential file..." -Type INFO
  296. # Collect the credentials to be used.
  297. $creds = Get-Credential
  298.  
  299. # Store the details in a hashed format
  300. $userName = $creds.UserName
  301. $passwordSecureString = $creds.Password
  302.  
  303. if($encryptMode -eq "DPAPI")
  304. {
  305. $password = $passwordSecureString | ConvertFrom-SecureString
  306. }
  307. elseif($encryptMode -eq "AES")
  308. {
  309. # Generate a random AES Encryption Key.
  310. $AESKey = New-Object Byte[] 32
  311. [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)
  312.  
  313. # Store the AESKey into a file. This file should be protected! (e.g. ACL on the file to allow only select people to read)
  314.  
  315. # Check if Credential File dir exists, if not, create it
  316. if(!(Test-Path $AESKeyFileDir))
  317. {
  318. New-Item -type Directory $AESKeyFileDir | out-null
  319. }
  320. Set-Content $AESKeyFilePath $AESKey # Any existing AES Key file will be overwritten
  321. $password = $passwordSecureString | ConvertFrom-SecureString -Key $AESKey
  322. }
  323. else
  324. {
  325. # Placeholder in case there are other EncryptModes
  326. }
  327.  
  328.  
  329. # Check if Credential File dir exists, if not, create it
  330. if(!(Test-Path $credentialFileDir))
  331. {
  332. New-Item -type Directory $credentialFileDir | out-null
  333. }
  334.  
  335. # Contents in this file can only be read and decrypted if you have the encryption key
  336. # If using DPAPI mode, then this can only be ready by the user that ran this script on the same machine
  337. # If using AES mode, then the AES.key file contains the encryption key
  338.  
  339. Set-Content $credentialFilePath $userName # Any existing credential file will be overwritten
  340. Add-Content $credentialFilePath $password
  341.  
  342. if($encryptMode -eq "AES")
  343. {
  344. 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."
  345. }
  346.  
  347. Write-Log "Credentials collected and stored." -Type INFO
  348. Write-Host -foreground Green "Credentials collected and stored."
  349. }
  350. catch
  351. {
  352. $errText = $error[0]
  353. Write-Log "Failed to Prepare Credentials. Error Message was: $errText" -type ERROR
  354. Write-Host -foreground red "Failed to Prepare Credentials. Please check Log File."
  355. Exit -1
  356. }
  357. }
  358.  
  359.  
  360. if($Execution -eq $true)
  361. {
  362.  
  363. $headerText = (Get-Date -format u) + "`t" + "INIT" + "`t" + "****$scriptName log initialised in Execution mode****"
  364. Start-LogFile -headerText $headerText # Initialize the log file with a header
  365.  
  366. # Check to ensure we have a secure credential file (i.e. -PrepareCredentials has been run) and that the contents are valid
  367. if(!(Test-Path $credentialFilePath))
  368. {
  369. Write-Log "Could not find a secure credential file at $credentialFilePath. Exiting." -Type ERROR
  370. 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."
  371. exit -1
  372. }
  373.  
  374. # Check to see if we have an AES Key file. If so, then we will use it to decrypt the secure credential file
  375. if(Test-Path $AESKeyFilePath)
  376. {
  377. try
  378. {
  379. Write-DebugLog "Found an AES Key File. Using this to decrypt the secure credential file."
  380. $decryptMode = "AES"
  381. $AESKey = Get-Content $AESKeyFilePath
  382. }
  383. catch
  384. {
  385. $errText = $error[0]
  386. Write-Log "AES Key file detected, but could not be read. Error Message was: $errText" -type ERROR
  387. exit -1
  388. }
  389. }
  390. else
  391. {
  392. Write-DebugLog "No AES Key File found. Using DPAPI method, which requires same user and machine to decrypt the secure credential file."
  393. $decryptMode = "DPAPI"
  394. }
  395.  
  396. try
  397. {
  398. Write-DebugLog "Reading secure credential file at $credentialFilePath."
  399. $credFiles = Get-Content $credentialFilePath
  400. $userName = $credFiles[0]
  401. if($decryptMode -eq "DPAPI")
  402. {
  403. $password = $credFiles[1] | ConvertTo-SecureString
  404. }
  405. elseif($decryptMode -eq "AES")
  406. {
  407. $password = $credFiles[1] | ConvertTo-SecureString -Key $AESKey
  408. }
  409. else
  410. {
  411. # Placeholder in case there are other decrypt modes
  412. }
  413.  
  414. Write-DebugLog "Creating credential object..."
  415. $credObject = New-Object System.Management.Automation.PSCredential -ArgumentList $userName, $password
  416. $passwordClearText = $credObject.GetNetworkCredential().Password
  417. Write-DebugLog "Credential store read. UserName is $userName and Password is $passwordClearText"
  418.  
  419.  
  420.  
  421. #**** Put Main Code here. The credentials you have stored is available as $credObject ****
  422.  
  423.  
  424. # Example Log File Entry
  425. Write-Log "No Type Flag creates an INFO entry"
  426. Write-Log "Or you can create a WARNING entry" -type "WARNING"
  427. Write-Log "Or an ERROR entry" -type "ERROR"
  428.  
  429. # Example Debug Log
  430. Write-DebugLog "This writes stuff if the -Debug flag is set in the script"
  431.  
  432.  
  433. }
  434. catch
  435. {
  436. $errText = $error[0]
  437. Write-Log "Could not execute in Execution mode. Error Message was: $errText" -type ERROR
  438. exit -1
  439. }
  440.  
  441. }
  442.  
  443. <##====================================================================================
  444. END OF CODE
  445. ##===================================================================================#>
Add Comment
Please, Sign In to add comment