From 1f3229e73ae32db30608cb7f72d46fbcf064ed58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20K=C5=82ys?= Date: Mon, 17 Feb 2020 20:36:55 +0100 Subject: [PATCH] Commit WIP --- GPOZaurr.psd1 | 123 ++++++++++++++++++++++ GPOZaurr.psm1 | 14 +++ Private/Get-XMLGPO.ps1 | 46 ++++++++ Private/Script.GPOPropertiesComputers.ps1 | 52 +++++++++ Private/Script.GPOPropertiesUsers.ps1 | 54 ++++++++++ Public/Get-GPOZaurr.ps1 | 33 ++++++ Public/Get-GPOZaurrPassword.ps1 | 116 ++++++++++++++++++++ Public/Invoke-GPOZaurr.ps1 | 6 ++ Public/Save-GPOZaurrFiles.ps1 | 26 +++++ README.md | 43 ++++++++ 10 files changed, 513 insertions(+) create mode 100644 GPOZaurr.psd1 create mode 100644 GPOZaurr.psm1 create mode 100644 Private/Get-XMLGPO.ps1 create mode 100644 Private/Script.GPOPropertiesComputers.ps1 create mode 100644 Private/Script.GPOPropertiesUsers.ps1 create mode 100644 Public/Get-GPOZaurr.ps1 create mode 100644 Public/Get-GPOZaurrPassword.ps1 create mode 100644 Public/Invoke-GPOZaurr.ps1 create mode 100644 Public/Save-GPOZaurrFiles.ps1 create mode 100644 README.md diff --git a/GPOZaurr.psd1 b/GPOZaurr.psd1 new file mode 100644 index 0000000..5a54783 --- /dev/null +++ b/GPOZaurr.psd1 @@ -0,0 +1,123 @@ +# +# Module manifest for module 'GPOZaurr' +# +# Generated by: Przemyslaw Klys +# +# Generated on: 17.02.2020 +# + +@{ + + # Script module or binary module file associated with this manifest. + RootModule = 'GPOZaurr.psm1' + + # Version number of this module. + ModuleVersion = '0.0.2' + + # Supported PSEditions + CompatiblePSEditions = 'Desktop' + + # ID used to uniquely identify this module + GUID = 'f7d4c9e4-0298-4f51-ad77-e8e3febebbde' + + # Author of this module + Author = 'Przemyslaw Klys' + + # Company or vendor of this module + CompanyName = 'Evotec' + + # Copyright statement for this module + Copyright = '(c) 2011 - 2020 Przemyslaw Klys @ Evotec. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'Group Policy Eater' + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '5.1' + + # Name of the Windows PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the Windows PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # DotNetFrameworkVersion = '' + + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # CLRVersion = '' + + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' + + # Modules that must be imported into the global environment prior to importing this module + RequiredModules = @(@{ModuleName = 'PSSharedGoods'; GUID = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe'; ModuleVersion = '0.0.123'; }) + + # Assemblies that must be loaded prior to importing this module + # RequiredAssemblies = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = 'Get-GPOZaurr', 'Get-GPOZaurrPassword', 'Invoke-GPOZaurr', + 'Save-GPOZaurrFiles' + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = @() + + # Variables to export from this module + # VariablesToExport = @() + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() + + # DSC resources to export from this module + # DscResourcesToExport = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'Windows', 'ActiveDirectory', 'GPO' + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/EvotecIT/GPOZaurr' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfo URI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' + +} \ No newline at end of file diff --git a/GPOZaurr.psm1 b/GPOZaurr.psm1 new file mode 100644 index 0000000..0d9e87f --- /dev/null +++ b/GPOZaurr.psm1 @@ -0,0 +1,14 @@ +#Get public and private function definition files. +$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue -Recurse ) +$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue -Recurse ) + +#Dot source the files +Foreach ($import in @($Public + $Private)) { + Try { + . $import.fullname + } Catch { + Write-Error -Message "Failed to import function $($import.fullname): $_" + } +} + +Export-ModuleMember -Function '*' -Alias '*' \ No newline at end of file diff --git a/Private/Get-XMLGPO.ps1 b/Private/Get-XMLGPO.ps1 new file mode 100644 index 0000000..72195a7 --- /dev/null +++ b/Private/Get-XMLGPO.ps1 @@ -0,0 +1,46 @@ +function Get-XMLGPO { + [cmdletBinding()] + param( + [XML] $XMLContent + ) + [PsCustomObject] @{ + 'Name' = $XMLContent.GPO.Name + 'Links' = $XMLContent.GPO.LinksTo #| Select-Object -ExpandProperty SOMPath + 'Enabled' = $XMLContent.GPO.GpoStatus + 'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText + + 'Domain' = $XMLContent.GPO.Identifier.Domain.'#text' + + 'ComputerSettingsAvailable' = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true } + 'ComputerSettingsStatus' = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" } + 'ComputerEnabled' = [bool] $XMLContent.GPO.Computer.Enabled + 'ComputerSetttingsVersionIdentical' = if ($XMLContent.GPO.Computer.VersionDirectory -eq $XMLContent.GPO.Computer.VersionSysvol) { $true } else { $false } + 'ComputerSettings' = $XMLContent.GPO.Computer.ExtensionData.Extension + + 'UserSettingsAvailable' = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true } + 'UserEnabled' = [bool] $XMLContent.GPO.User.Enabled + 'UserSettingsStatus' = if ($XMLContent.GPO.User.VersionDirectory -eq 0 -and $XMLContent.GPO.User.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" } + 'UserSettingsVersionIdentical' = if ($XMLContent.GPO.User.VersionDirectory -eq $XMLContent.GPO.User.VersionSysvol) { $true } else { $false } + 'UserSettings' = $XMLContent.GPO.User.ExtensionData.Extension + + + # 'CreationTime' = [DateTime] $XMLContent.GPO.CreatedTime + # 'ModificationTime' = [DateTime] $XMLContent.GPO.ModifiedTime + # 'ReadTime' = [DateTime] $XMLContent.GPO.ReadTime + + 'WMIFilter' = $GPO.WmiFilter.name + 'WMIFilterDescription' = $GPO.WmiFilter.Description + 'Path' = $GPO.Path + #'SDDL' = if ($Splitter -ne '') { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' -join $Splitter } else { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' } + 'ACL' = $XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process { + [PsCustomObject] @{ + 'User' = $_.trustee.name.'#Text' + 'Permission Type' = $_.type.PermissionType + 'Inherited' = $_.Inherited + 'Permissions' = $_.Standard.GPOGroupedAccessEnum + } + } + + } + #break +} \ No newline at end of file diff --git a/Private/Script.GPOPropertiesComputers.ps1 b/Private/Script.GPOPropertiesComputers.ps1 new file mode 100644 index 0000000..6d88234 --- /dev/null +++ b/Private/Script.GPOPropertiesComputers.ps1 @@ -0,0 +1,52 @@ +$Script:GPOPropetiesComputers = [ordered] @{ + 'Account' = '' + 'Audit' = '' + 'AuditSetting' = '' + 'AutoEnrollmentSettings' = '' + 'Blocked' = '' + 'certSettingsTrustedPublishers' = '' + 'DataSourcesSettings' = '' + 'DomainProfile' = '' + 'Dot3SvcSetting' = '' + 'EFSRecoveryAgent' = '' + 'EFSSettings' = '' + 'EnvironmentVariables' = '' + 'EventLog' = '' + 'File' = '' + 'FilesSettings' = '' + 'Folders' = '' + 'General' = '' + 'Global' = '' + 'GlobalSettings' = '' + 'InboundFirewallRules' = '' + 'IntermediateCACertificate' = '' + 'InternetZoneRule' = '' + 'LocalUsersAndGroups' = '' + 'MsiApplication' = '' + 'NetworkOptions' = '' + 'NetworkShares' = '' + 'NTServices' = '' + 'OutboundFirewallRules' = '' + 'PathRule' = '' + 'Policy' = '' + 'PowerOptions' = '' + 'PrinterConnection' = '' + 'Printers' = '' + 'PrivateProfile' = '' + 'PublicProfile' = '' + 'Registry' = '' + 'RegistrySetting' = '' + 'RegistrySettings' = '' + 'RestrictedGroups' = '' + 'RootCertificate' = '' + 'RootCertificateSettings' = '' + 'ScheduledTasks' = '' + 'Script' = '' + 'SecurityOptions' = '' + 'ShortcutSettings' = '' + 'SystemServices' = '' + 'TrustedPublishersCertificate' = '' + 'type' = '' + 'UserRightsAssignment' = '' + 'WLanSvcSetting' = '' +} \ No newline at end of file diff --git a/Private/Script.GPOPropertiesUsers.ps1 b/Private/Script.GPOPropertiesUsers.ps1 new file mode 100644 index 0000000..450dae2 --- /dev/null +++ b/Private/Script.GPOPropertiesUsers.ps1 @@ -0,0 +1,54 @@ +$Script:GPOPropertiesUsers = [ordered] @{ + 'AutoDetectConfigSettings' = '' + 'AutoEnrollmentSettings' = '' + 'AutomaticConfiguration' = '' + 'AutoSetupSetting' = '' + 'Blocked' = '' + 'BrowserTitle' = '' + 'CustomSetupSetting' = '' + 'DataSourcesSettings' = '' + 'DefinesConnectionSettings' = '' + 'DefinesEscOffSettings' = '' + 'DefinesEscOnSettings' = '' + 'DeleteChannels' = '' + 'DriveMapSettings' = '' + 'EscOffLocalSites' = '' + 'EscOffSecurityZoneAndPrivacy' = '' + 'EscOffTrustedSites' = '' + 'EscOnLocalSites' = '' + 'EscOnSecurityZoneAndPrivacy' = '' + 'EscOnTrustedSites' = '' + 'FavoriteURL' = '' + 'FilesSettings' = '' + 'Folder' = '' + 'FolderOptions' = '' + 'Folders' = '' + 'General' = '' + 'HomePage' = '' + 'ImportedContentRatings' = '' + 'InternetOptions' = '' + 'LocalUsersAndGroups' = '' + 'MsiApplication' = '' + 'NetworkOptions' = '' + 'PathRule' = '' + 'PlaceFavoritesAtTop' = '' + 'Policy' = '' + 'PowerOptions' = '' + 'PreferenceMode' = '' + 'PrinterConnection' = '' + 'Printers' = '' + 'Programs' = '' + 'ProxySettings' = '' + 'RegionalOptionsSettings' = '' + 'RegistrySetting' = '' + 'RegistrySettings' = '' + 'RestartSetupSetting' = '' + 'ScheduledTasks' = '' + 'Script' = '' + 'SearchBar' = '' + 'ShortcutSettings' = '' + 'StartMenuSettings' = '' + 'ToolsSetting' = '' + 'TrustedPublisherLockdown' = '' + 'type' = '' +} \ No newline at end of file diff --git a/Public/Get-GPOZaurr.ps1 b/Public/Get-GPOZaurr.ps1 new file mode 100644 index 0000000..5f483e8 --- /dev/null +++ b/Public/Get-GPOZaurr.ps1 @@ -0,0 +1,33 @@ +function Get-GPOZaurr { + [cmdletBinding()] + param( + [alias('ForestName')][string] $Forest, + [string[]] $ExcludeDomains, + [alias('Domain', 'Domains')][string[]] $IncludeDomains, + [System.Collections.IDictionary] $ExtendedForestInformation, + [string[]] $GPOPath + ) + + if (-not $GPOPath) { + if (-not $ExtendedForestInformation) { + $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains + } else { + $ForestInformation = $ExtendedForestInformation + } + + foreach ($Domain in $ForestInformation.Domains) { + Get-GPO -All -Server $ForestInformation.QueryServers[$Domain] -Domain $Domain | ForEach-Object { + $XMLContent = Get-GPOReport -ID $_.ID -ReportType XML -Server $ForestInformation.QueryServers[$Domain] -Domain $Domain + Get-XMLGPO -XMLContent $XMLContent + } + } + } else { + foreach ($Path in $GPOPath) { + Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml | ForEach-Object { + $XMLContent = [XML]::new() + $XMLContent.Load($_.FullName) + Get-XMLGPO -XMLContent $XMLContent + } + } + } +} diff --git a/Public/Get-GPOZaurrPassword.ps1 b/Public/Get-GPOZaurrPassword.ps1 new file mode 100644 index 0000000..5479c71 --- /dev/null +++ b/Public/Get-GPOZaurrPassword.ps1 @@ -0,0 +1,116 @@ +function Get-GPOZaurrPassword { + [cmdletBinding()] + param( + [alias('ForestName')][string] $Forest, + [string[]] $ExcludeDomains, + [alias('Domain', 'Domains')][string[]] $IncludeDomains, + [System.Collections.IDictionary] $ExtendedForestInformation, + [string[]] $GPOPath + ) + + if (-not $GPOPath) { + if (-not $ExtendedForestInformation) { + $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains + } else { + $ForestInformation = $ExtendedForestInformation + } + + $GPOPath = foreach ($Domain in $ForestInformation.Domains) { + -join ('\\', $Domain, '\SYSVOL\', $Domain, '\Policies') + } + } + + if (-not $GPOPath) { + return + } + + foreach ($Path in $GPOPath) { + #Extract the all XML files in the Folders + $Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml + $Output = foreach ($XMLFileName in $Items) { + #Convert XML in a String file + [string]$XMLString = Get-Content ($XMLFileName.FullName) + #Check if Cpassword Exist in the file + if ($XMLString.Contains("cpassword")) { + #Take the Cpassword Value from XML String file + [string]$Cpassword = [regex]::matches($XMLString, '(cpassword=).+?(?=\")') + $Cpassword = $Cpassword.split('(\")')[1] + #Check if Cpassword has a value + if ($Cpassword.Length -gt 20 -and $Cpassword -notlike '*cpassword*') { + $Mod = ($Cpassword.length % 4) + switch ($Mod) { + '1' { $Cpassword = $Cpassword.Substring(0, $Cpassword.Length - 1) } + '2' { $Cpassword += ('=' * (4 - $Mod)) } + '3' { $Cpassword += ('=' * (4 - $Mod)) } + } + $Base64Decoded = [Convert]::FromBase64String($Cpassword) + $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider + #Use th AES Key + [Byte[]] $AesKey = @(0x4e, 0x99, 0x06, 0xe8, 0xfc, 0xb6, 0x6c, 0xc9, 0xfa, 0xf4, 0x93, 0x10, 0x62, 0x0f, 0xfe, 0xe8, 0xf4, 0x96, 0xe8, 0x06, 0xcc, 0x05, 0x79, 0x90, 0x20, 0x9b, 0x09, 0xa4, 0x33, 0xb6, 0x6c, 0x1b) + $AesIV = New-Object Byte[]($AesObject.IV.Length) + $AesObject.IV = $AesIV + $AesObject.Key = $AesKey + $DecryptorObject = $AesObject.CreateDecryptor() + [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length) + #Convert Hash variable in a String valute + $Password = [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock) + #[string]$GPOguid = [regex]::matches($XMLFileName.DirectoryName, '(?<=\{).+?(?=\})') + #$GPODetail = Get-GPO -guid $GPOguid + [xml] $XMLContent = $XMLString + + if (-not $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.Properties.cpassword -and -not $XMLContent.gpo.User.ExtensionData.Extension.DriveMapSettings.Drive.Properties.cpassword) { + Write-Host '' + } + + + [PsCustomObject] @{ + 'Name' = $XMLContent.GPO.Name + 'Links' = $XMLContent.GPO.LinksTo #| Select-Object -ExpandProperty SOMPath + 'Enabled' = $XMLContent.GPO.GpoStatus + #'GPO' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups + 'User' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.name + 'Cpassword' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.Properties.cpassword + 'CpasswordMap' = $XMLContent.gpo.User.ExtensionData.Extension.DriveMapSettings.Drive.Properties.cpassword + 'Password' = $Password + 'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText + + 'Domain' = $XMLContent.GPO.Identifier.Domain + + 'ComputerSettingsAvailable' = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true } + 'ComputerSettingsStatus' = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" } + 'ComputerEnabled' = [bool] $XMLContent.GPO.Computer.Enabled + 'ComputerSetttingsVersionIdentical' = if ($XMLContent.GPO.Computer.VersionDirectory -eq $XMLContent.GPO.Computer.VersionSysvol) { $true } else { $false } + 'ComputerSettings' = $XMLContent.GPO.Computer.ExtensionData.Extension + + 'UserSettingsAvailable' = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true } + 'UserEnabled' = [bool] $XMLContent.GPO.User.Enabled + 'UserSettingsStatus' = if ($XMLContent.GPO.User.VersionDirectory -eq 0 -and $XMLContent.GPO.User.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" } + 'UserSettingsVersionIdentical' = if ($XMLContent.GPO.User.VersionDirectory -eq $XMLContent.GPO.User.VersionSysvol) { $true } else { $false } + 'UserSettings' = $XMLContent.GPO.User.ExtensionData.Extension + + + 'CreationTime' = [DateTime] $XMLContent.GPO.CreatedTime + 'ModificationTime' = [DateTime] $XMLContent.GPO.ModifiedTime + 'ReadTime' = [DateTime] $XMLContent.GPO.ReadTime + + 'WMIFilter' = $GPO.WmiFilter.name + 'WMIFilterDescription' = $GPO.WmiFilter.Description + 'Path' = $GPO.Path + #'SDDL' = if ($Splitter -ne '') { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' -join $Splitter } else { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' } + 'ACL' = $XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process { + [PSCustomObject] @{ + 'User' = $_.trustee.name.'#Text' + 'Permission Type' = $_.type.PermissionType + 'Inherited' = $_.Inherited + 'Permissions' = $_.Standard.GPOGroupedAccessEnum + } + } + + } + #Write-Host "I find a Password [ " $Password " ] The GPO named:" $GPODetail" and th file is:" $XMLFileName + } + } #if($XMLContent.Contains("cpassword") + } + $Output + } +} \ No newline at end of file diff --git a/Public/Invoke-GPOZaurr.ps1 b/Public/Invoke-GPOZaurr.ps1 new file mode 100644 index 0000000..d58fbb4 --- /dev/null +++ b/Public/Invoke-GPOZaurr.ps1 @@ -0,0 +1,6 @@ +function Invoke-GPOZaurr { + [cmdletBinding()] + param( + + ) +} \ No newline at end of file diff --git a/Public/Save-GPOZaurrFiles.ps1 b/Public/Save-GPOZaurrFiles.ps1 new file mode 100644 index 0000000..2bdf851 --- /dev/null +++ b/Public/Save-GPOZaurrFiles.ps1 @@ -0,0 +1,26 @@ +function Save-GPOZaurrFiles { + [cmdletBinding()] + param( + [alias('ForestName')][string] $Forest, + [string[]] $ExcludeDomains, + [alias('Domain', 'Domains')][string[]] $IncludeDomains, + [System.Collections.IDictionary] $ExtendedForestInformation, + [string[]] $GPOPath + ) + if ($GPOPath) { + if (-not $ExtendedForestInformation) { + $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains + } else { + $ForestInformation = $ExtendedForestInformation + } + $null = New-Item -ItemType Directory -Path $GPOPath -Force + foreach ($Domain in $ForestInformation.Domains) { + Get-GPO -All -Server $ForestInformation.QueryServers[$Domain] -Domain $Domain | ForEach-Object { + $XMLContent = Get-GPOReport -ID $_.ID.Guid -ReportType XML -Server $ForestInformation.QueryServers[$Domain] -Domain $Domain + $Path = [io.path]::Combine($GPOPath, "$($_.ID.Guid).xml") + + $XMLContent | Set-Content -LiteralPath $Path -Force -Encoding Unicode + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6cce4a9 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +

+ + + +

+ +

+ + + + +

+ +

+ + + +

+ +# GPOZaurr + +## To install + +```powershell +Install-Module -Name GPOZaurr -AllowClobber -Force +``` + +Force and AllowClobber aren't necessary, but they do skip errors in case some appear. + +## And to update + +```powershell +Update-Module -Name GPOZaurr +``` + +That's it. Whenever there's a new version, you run the command, and you can enjoy it. Remember that you may need to close, reopen PowerShell session if you have already used module before updating it. + +**The essential thing** is if something works for you on production, keep using it till you test the new version on a test computer. I do changes that may not be big, but big enough that auto-update may break your code. For example, small rename to a parameter and your code stops working! Be responsible! + +## Changelog + +- 0.0.1 - Unreleased + - Initial release