Pagootle.psm1
#Region '.\private\Compare-SecureString.ps1' -1 function Compare-SecureString { <# .NOTES From https://stackoverflow.com/a/48810852 #> param( [Security.SecureString] $secureString1, [Security.SecureString] $secureString2 ) try { $bstr1 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString1) $bstr2 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString2) $length1 = [Runtime.InteropServices.Marshal]::ReadInt32($bstr1,-4) $length2 = [Runtime.InteropServices.Marshal]::ReadInt32($bstr2,-4) if ( $length1 -ne $length2 ) { return $false } for ( $i = 0; $i -lt $length1; ++$i ) { $b1 = [Runtime.InteropServices.Marshal]::ReadByte($bstr1,$i) $b2 = [Runtime.InteropServices.Marshal]::ReadByte($bstr2,$i) if ( $b1 -ne $b2 ) { return $false } } return $true } finally { if ( $bstr1 -ne [IntPtr]::Zero ) { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr1) } if ( $bstr2 -ne [IntPtr]::Zero ) { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr2) } } } #EndRegion '.\private\Compare-SecureString.ps1' 39 #Region '.\private\ConvertTo-XmlString.ps1' -1 function ConvertTo-XmlString { [CmdletBinding()] Param( [Parameter(Mandatory)] [String[]] $Group ) process { $groupsXml = New-Object System.Xml.XmlDocument $root = $groupsXml.CreateElement("groups") $null = $groupsXml.AppendChild($root) foreach ($g in $group) { $groupElement = $groupsXml.CreateElement("group") $groupNameElement = $groupsXml.CreateElement("GroupName") $groupNameElement.InnerText = $g $null = $groupElement.AppendChild($groupNameElement) $null = $root.AppendChild($groupElement) } $groupsXml.OuterXml } } #EndRegion '.\private\ConvertTo-XmlString.ps1' 28 #Region '.\private\CopyMaxBytes.ps1' -1 function CopyMaxBytes { [CmdletBinding()] param( [Parameter()] [System.IO.FileStream] $source, [Parameter()] [System.IO.Stream] $target, [Parameter()] [Int] $maxBytes, [Parameter()] [Int] $startOffset, [Parameter()] [Int] $totalSize ) end { $buffer = [byte[]]::CreateInstance([byte], 32767) $totalBytesRead = 0 while ($true) { $bytesRead = $source.Read($buffer, 0, [Math]::Min($maxBytes - $totalBytesRead, $buffer.Length)) if (!$bytesRead) { break } $target.Write($buffer, 0, $bytesRead) $totalBytesRead += $bytesRead if ($totalBytesRead -ge $maxBytes) { break } $overallProgress = $startOffset + $totalBytesRead Write-Progress -Activity "Uploading $fileName..." -Status "$overallProgress/$totalSize" -PercentComplete ($overallProgress / $totalSize * 100) } } } #EndRegion '.\private\CopyMaxBytes.ps1' 38 #Region '.\private\Get-ProGetConnectionString.ps1' -1 function Get-ProGetConnectionString { [CmdletBinding()] Param( [Parameter()] [ValidateScript({ Test-Path $_ })] [String] $ConfigFile = "C:\ProgramData\Inedo\SharedConfig\ProGet.config" ) end { [xml]$xml = Get-Content $ConfigFile $xml.InedoAppConfig.ConnectionString } } #EndRegion '.\private\Get-ProGetConnectionString.ps1' 16 #Region '.\private\Invoke-CreateGroupStoredProc.ps1' -1 function Invoke-CreateGroupStoredProc { [CmdletBinding()] Param( [Parameter(Mandatory)] [String] $Name ) end { $connectionString = Get-ProGetConnectionString # Define the stored procedure and parameters $storedProc = "dbo.Users_CreateGroup" $groupName = $Name # Create and open SQL connection $sqlConnection = New-Object System.Data.SqlClient.SqlConnection($connectionString) $sqlConnection.Open() # Create SqlCommand and specify it as a stored procedure $sqlCommand = $sqlConnection.CreateCommand() $sqlCommand.CommandText = $storedProc $sqlCommand.CommandType = [System.Data.CommandType]::StoredProcedure # Add parameter $sqlCommand.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@Group_Name", [System.Data.SqlDbType]::NVarChar, 50))).Value = $groupName # Execute the SqlCommand $sqlCommand.ExecuteNonQuery() # Close the connection $sqlConnection.Close() Write-Host "Group added successfully to ProGet." } } #EndRegion '.\private\Invoke-CreateGroupStoredProc.ps1' 39 #Region '.\private\Invoke-GetTaskStoredProc.ps1' -1 function Invoke-GetTaskStoredProc { [CmdletBinding()] Param() end { # Execute locally Add-Type -AssemblyName "System.Data" $StoredProcedure = 'dbo.Security_GetTasks' $ConnectionString = Get-ProGetConnectionString $connection = [System.Data.SqlClient.SqlConnection]::new($ConnectionString) $connection.Open() try { $command = $connection.CreateCommand() $command.CommandType = [System.Data.CommandType]::StoredProcedure $command.CommandText = $StoredProcedure $reader = $command.ExecuteReader() $results = [System.Collections.Generic.List[pscustomobject]]::new() while ($reader.Read()) { $row = @{} for ($i = 0; $i -lt $reader.FieldCount; $i++) { $column = $reader.GetName($i) $row[$column] = $reader.GetValue($i) } $results.Add([PSCustomObject]$row) } $reader.Close() $results } catch { Write-Error "An error occurred: $_" } finally { $connection.Close() } } } #EndRegion '.\private\Invoke-GetTaskStoredProc.ps1' 42 #Region '.\private\Invoke-GetUserStoredProc.ps1' -1 function Invoke-GetUserStoredProc { <# .SYNOPSIS Private function that executes dbo.Users_GetUsers stored procedure in ProGet Database #> [CmdletBinding()] Param( [Parameter()] [String] $Username ) end { # Execute locally Add-Type -AssemblyName "System.Data" $StoredProcedure = 'dbo.Users_GetUsers' $ConnectionString = 'Server=Localhost\SQLEXPRESS;Database=ProGet;Trusted_Connection=true;' $connection = [System.Data.SqlClient.SqlConnection]::new($ConnectionString) $connection.Open() try { $command = $connection.CreateCommand() $command.CommandType = [System.Data.CommandType]::StoredProcedure $command.CommandText = $StoredProcedure $parameter = $command.Parameters.Add("@User_Name", [System.Data.SqlDbType]::NVarChar, 50) $parameter.Value = if ($Username) { $Username } else { [DBNull]::Value } $reader = $command.ExecuteReader() $results = [System.Collections.Generic.List[pscustomobject]]::new() while ($reader.Read()) { $row = @{} for ($i = 0; $i -lt $reader.FieldCount; $i++) { $column = $reader.GetName($i) $exclude = @('Password_Bytes', 'Salt_Bytes') if ($column -notin $exclude) { $row[$column] = $reader.GetValue($i) } } $results.Add([PSCustomObject]$row) } $reader.Close() $results } catch { Write-Error "An error occurred: $_" } finally { $connection.Close() } } } #EndRegion '.\private\Invoke-GetUserStoredProc.ps1' 62 #Region '.\private\Invoke-NewDropPathStoredProc.ps1' -1 function Invoke-NewDropPathStoredProc { [CmdletBinding()] Param( [Parameter()] [String] $Feed, [Parameter()] [String] $DropPath = 'C:\drop' ) # Create the Drop Path directory if (-not $PSBoundParameters.ContainsKey('DropPath')) { $DropPath = Join-Path $DropPath -ChildPath $Feed } if (-not (Test-Path $DropPath)) { $null = New-Item $DropPath -ItemType Directory } # Assign permissions to the Inedo service user to the Drop Path $ServiceUser = (Get-CimInstance Win32_Service -Filter "Name = 'INEDOPROGETWEBSVC'").StartName Set-ServiceUserPermission -FilePath $DropPath -ServiceUser $ServiceUser -Permissions Modify # Create SQL query with parameterized stored procedure execution $query = @" DECLARE @Feed_Id INT SET @Feed_Id = (SELECT Feed_Id FROM Feeds WHERE Feed_Name = @Feed) EXEC [dbo].[Feeds_SetFeedProperty] @Feed_Id = @Feed_Id, @DropPath_Text = @DropPath "@ # Define SQL connection $connectionString = Get-ProGetConnectionString $connection = New-Object System.Data.SqlClient.SqlConnection $connection.ConnectionString = $connectionString $connection.Open() # Create and execute SQL command $command = $connection.CreateCommand() $command.CommandText = $query $null = $command.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@Feed", $Feed))) $null = $command.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@DropPath", $DropPath))) $null = $command.ExecuteNonQuery() # Close connection $connection.Close() } #EndRegion '.\private\Invoke-NewDropPathStoredProc.ps1' 53 #Region '.\private\Invoke-NewUserStoredProc.ps1' -1 function Invoke-NewUserStoredProc { <# .SYNOPSIS Private function that executes dbo.Users_CreateOrUpdateUser stored procedure in ProGet Database #> param ( [Parameter()] [string] $ConnectionString = 'Server=Localhost\SQLEXPRESS;Database=ProGet;Trusted_Connection=true;', [Parameter()] [string] $StoredProcedure = 'dbo.Users_CreateOrUpdateUser', [Parameter(Mandatory)] [Hashtable] $Params, [Parameter()] [switch] $UseRemoting, [Parameter()] [string] $RemoteComputer, [Parameter()] [pscredential] $Credential ) end { if ($UseRemoting) { # Execute on a remote machine using PowerShell remoting $scriptBlock = { param ($ConnectionString, $StoredProcedure, $Params) Add-Type -AssemblyName "System.Data" $connection = New-Object System.Data.SqlClient.SqlConnection $connection.ConnectionString = $ConnectionString $connection.Open() try { $command = $connection.CreateCommand() $command.CommandType = [System.Data.CommandType]::StoredProcedure $command.CommandText = $StoredProcedure foreach ($key in $Params.Keys) { $command.Parameters.AddWithValue($key, $Params[$key]) | Out-Null } $null = $command.ExecuteNonQuery() } catch { Write-Error "An error occurred: $_" } finally { $connection.Close() } } if ($Credential) { Invoke-Command -ComputerName $RemoteComputer -Credential $Credential -ScriptBlock $scriptBlock -ArgumentList $ConnectionString, $StoredProcedure, $Params } else { Invoke-Command -ComputerName $RemoteComputer -ScriptBlock $scriptBlock -ArgumentList $ConnectionString, $StoredProcedure, $Params } } else { # Execute locally Add-Type -AssemblyName "System.Data" $connection = New-Object System.Data.SqlClient.SqlConnection $connection.ConnectionString = $ConnectionString $connection.Open() try { $command = $connection.CreateCommand() $command.CommandType = [System.Data.CommandType]::StoredProcedure $command.CommandText = $StoredProcedure foreach ($key in $Params.Keys) { $command.Parameters.AddWithValue($key, $Params[$key]) | Out-Null } $null = $command.ExecuteNonQuery() } catch { Write-Error "An error occurred: $_" } finally { $connection.Close() } } } } #EndRegion '.\private\Invoke-NewUserStoredProc.ps1' 99 #Region '.\private\Invoke-ProGet.ps1' -1 function Invoke-ProGet { [CmdletBinding()] Param( [Parameter(Mandatory, Position = 0)] [String] $Slug, [Parameter()] [String] $Method = 'GET', [Parameter()] [Hashtable] $Body, [Parameter()] [Hashtable] $Form, [Parameter()] [String] $BodyJson, [Parameter()] [String] $File, [Parameter()] [String] $ContentType = 'application/json' ) begin { $Configuration = Get-ProGetConfiguration # If we call this from New-ProGetFeed we have to use a special API key because ProGet is...well, silly. $caller = (Get-PSCallStack)[1].Command } end { $ssl = if ($Configuration['UseSSL']) { @{Protocol = 'https' ; Port = $Configuration['SslPort'] } } else { @{Protocol = 'http'; Port = $Configuration['NonSslPort'] } } $Uri = '{0}:{1}{2}' -f "$($ssl['Protocol'])://$($Configuration['Hostname'])", $ssl['Port'], $Slug.TrimEnd('/') Write-Verbose -Message $Uri $params = @{ Uri = $Uri Method = $Method ContentType = $ContentType Headers = if ($caller -ne 'New-ProGetFeed') { @{'X-ApiKey' = $Configuration.Credential.GetNetworkCredential().Password } } else { @{'X-ApiKey' = $Configuration.ApiKey.GetNetworkCredential().Password } } SkipCertificateCheck = $true Verbose = $false } if ($Body) { $params['Body'] = $Body | ConvertTo-Json -Depth 5 } if ($BodyJson) { $params['Body'] = $BodyJson } if ($Form) { $params['Form'] = $Form $params.Remove('ContentType') } if ($File) { $fileContent = [System.IO.File]::ReadAllBytes($File) $params['Body'] = $fileContent $params['ContentType'] = 'application/octet-stream' Write-Verbose $params.Uri } #Write-Verbose ($Body | ConvertTo-Json -Depth 5) Invoke-RestMethod @params } } #EndRegion '.\private\Invoke-ProGet.ps1' 89 #Region '.\private\Invoke-UserPasswordStoredProc.ps1' -1 function Invoke-UserPasswordStoredProc { <# .SYNOPSIS Private function that executes dbo.Users_SetPassword stored procedure in ProGet Database #> [CmdletBinding()] Param( [Parameter(Mandatory)] [Hashtable] $Params, [Parameter(DontShow)] [String] $ConnectionString = 'Server=Localhost\SQLEXPRESS;Database=ProGet;Trusted_Connection=true;' ) end { $StoredProcedure = 'dbo.Users_SetPassword' if ($UseRemoting) { # Execute on a remote machine using PowerShell remoting $scriptBlock = { param ($ConnectionString, $StoredProcedure, $Params) Add-Type -AssemblyName "System.Data" $connection = New-Object System.Data.SqlClient.SqlConnection $connection.ConnectionString = $ConnectionString $connection.Open() try { $command = $connection.CreateCommand() $command.CommandType = [System.Data.CommandType]::StoredProcedure $command.CommandText = $StoredProcedure $command.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@User_Name", [Data.SqlDbType]::NVarChar, 50))).Value = $Params['User_Name'] $command.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@Password_Bytes", [Data.SqlDbType]::Binary, 20))).Value = $Params['Password_Bytes'] $command.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@Salt_Bytes", [Data.SqlDbType]::Binary, 10))).Value = $Params['Salt_Bytes'] $null = $command.ExecuteNonQuery() } catch { Write-Error "An error occurred: $_" } finally { $connection.Close() } } if ($Credential) { Invoke-Command -ComputerName $RemoteComputer -Credential $Credential -ScriptBlock $scriptBlock -ArgumentList $ConnectionString, $StoredProcedure, $Params } else { Invoke-Command -ComputerName $RemoteComputer -ScriptBlock $scriptBlock -ArgumentList $ConnectionString, $StoredProcedure, $Params } } else { # Execute locally Add-Type -AssemblyName "System.Data" $connection = New-Object System.Data.SqlClient.SqlConnection $connection.ConnectionString = $ConnectionString $connection.Open() try { $command = $connection.CreateCommand() $command.CommandType = [System.Data.CommandType]::StoredProcedure $command.CommandText = $StoredProcedure $command.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@User_Name", [Data.SqlDbType]::NVarChar, 50))).Value = $Params['User_Name'] $command.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@Password_Bytes", [Data.SqlDbType]::Binary, 20))).Value = $Params['Password_Bytes'] $command.Parameters.Add((New-Object Data.SqlClient.SqlParameter("@Salt_Bytes", [Data.SqlDbType]::Binary, 10))).Value = $Params['Salt_Bytes'] $null = $command.ExecuteNonQuery() } catch { Write-Error "An error occurred: $_" } finally { $connection.Close() } } } } #EndRegion '.\private\Invoke-UserPasswordStoredProc.ps1' 85 #Region '.\private\Set-CertPermissions.ps1' -1 function Set-CertPermissions { <# .SYNOPSIS Sets the permissions on the private key for a given certificate .DESCRIPTION Long description .PARAMETER Thumbprint Parameter description .PARAMETER Location Parameter description .PARAMETER Store Parameter description .PARAMETER ServiceUser Parameter description .EXAMPLE An example .NOTES General notes #> [CmdletBinding()] Param( [Parameter(Mandatory)] [String] $Thumbprint, [Parameter(Mandatory)] [String] $Location, [Parameter(Mandatory)] [String] $Store, [Parameter(Mandatory)] [String] $ServiceUser ) end { # Load the certificate from the specified store $certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store $Store, $Location $certStore.Open("ReadOnly") $certificate = $certStore.Certificates | Where-Object { $_.Thumbprint -eq $thumbprint } if (-not $certificate) { Write-Error "Certificate with thumbprint $thumbprint not found in LocalMachine\My." $certStore.Close() return } # Get the private key file path $privateKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($certificate) $uniqueName = $privateKey.key.uniquename $keyPath = Join-Path $env:ProgramData "Microsoft\Crypto\RSA\MachineKeys\$($uniquename)" # Close the certificate store if we have a private key, otherwise error that there is a problem finding a private key if (Test-Path $keyPath) { $certStore.Close() } else { Write-Error "Unable to locate the private key for the certificate." $certStore.Close() return } # Grant Inedo Service user read access to the private key $ServiceUser = (Get-CimInstance Win32_Service -Filter "Name = 'INEDOPROGETWEBSVC'").StartName Set-ServiceUserPermission -FilePath $keyPath -ServiceUser $ServiceUser -Permissions Read } } #EndRegion '.\private\Set-CertPermissions.ps1' 79 #Region '.\private\Set-ServiceUserPermission.ps1' -1 function Set-ServiceUserPermission { [CmdletBinding()] Param( [Parameter(Mandatory)] [String] $FilePath, [Parameter(Mandatory)] [String] $ServiceUser, [Parameter(Mandatory)] [ValidateSet('Read', 'ReadExecute', 'Modify', 'FullControl')] [String] $Permissions ) begin { $caller = (Get-PSCallStack)[1].Command } end { $acl = Get-Acl $FilePath $inheritanceFlags = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit $PermissionSet = @{ Read = [System.Security.AccessControl.FileSystemRights]::Read ReadExecute = [System.Security.AccessControl.FileSystemRights]::ReadAndExecute Modify = [System.Security.AccessControl.FileSystemRights]::Modify FullControl = [System.Security.AccessControl.FileSystemRights]::FullControl } if ($caller -eq 'Set-CertPermissions') { # Grant ProGet service user read access to the private key $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule( $ServiceUser, # User $PermissionSet[$Permissions], # Permissions "Allow" ) } else { $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule( $ServiceUser, # User $PermissionSet[$Permissions], # Permissions $inheritanceFlags, # Inheritance [System.Security.AccessControl.PropagationFlags]::None, #Apply normal inheritance "Allow" ) } $acl.SetAccessRule($accessRule) Set-Acl -Path $FilePath -AclObject $acl } } #EndRegion '.\private\Set-ServiceUserPermission.ps1' 55 #Region '.\public\Assets\New-ProGetAsset.ps1' -1 function New-ProGetAsset { <# .Synopsis Transfers a file to a ProGet asset directory. .Description Transfers a file to a ProGet asset directory. This function performs automatic chunking if the file is larger than a specified threshold. .Parameter FileName Name of the file to upload from the local file system. .Parameter EndpointUrl Full URL of the ProGet asset directory's API endpoint. This is typically something like http://proget/endpoints/<directoryname> .Parameter AssetName Full path of the asset to create in ProGet's asset directory. .Parameter ChunkSize Uploads larger than this value will be uploaded using multiple requests. The default is 5 MB. .Example New-ProGetAsset -FileName C:\Files\Image.jpg -AssetName images/image.jpg -EndpointUrl http://proget/endpoints/MyAssetDir #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Commands/New-ProGetAsset')] param( [Parameter(Mandatory = $true)] [string] $fileName, [Parameter()] [String] $AssetDirectory, [Parameter()] [string] $Slug = '/endpoints/{0}' -f $AssetDirectory, [Parameter(Mandatory = $true)] [string] $assetName, [Parameter()] [int] $chunkSize = 5 * 1024 * 1024 ) begin { $Configuration = Get-ProGetConfiguration $ssl = if ($Configuration['UseSSL']) { 'https' } else { 'http' } } process { $endpointUrl = "$($ssl)://$($Configuration['Hostname']):8443" if (-not $endpointUrl.EndsWith('/')) { $endpointUrl += '/' } $targetUrl = $endpointUrl + 'content/' + "$AssetDirectory/" + [Uri]::EscapeUriString($assetName.Replace('\', '/')) Write-Verbose $targetUrl $fileInfo = Get-ChildItem -Path $fileName if ($fileInfo.Length -le $chunkSize) { Invoke-WebRequest -Method Post -Uri $targetUrl -InFile $fileName } else { $sourceStream = [System.IO.FileStream]::new($fileName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read, 4096, [System.IO.FileOptions]::SequentialScan) try { $fileLength = $sourceStream.Length $remainder = 0 $totalParts = [Math]::DivRem($fileLength, $chunkSize, [ref]$remainder) if ($remainder -ne 0) { $totalParts++ } $uuid = [Guid]::NewGuid().ToString("N") 0..($totalParts - 1) | ForEach-Object { $index = $_ $offset = $index * $chunkSize $currentChunkSize = if ($index -eq ($totalParts - 1)) { $fileLength - $offset } else { $chunkSize } $req = [System.Net.WebRequest]::CreateHttp("${targetUrl}?multipart=upload&id=$uuid&index=$index&offset=$offset&totalSize=$fileLength&partSize=$currentChunkSize&totalParts=$totalParts") $req.Method = 'POST' $req.ContentLength = $currentChunkSize $req.AllowWriteStreamBuffering = $false $reqStream = $req.GetRequestStream() try { CopyMaxBytes -source $sourceStream -target $reqStream -maxBytes $currentChunkSize -startOffset $offset -totalSize $fileLength } finally { if ($reqStream) { $reqStream.Dispose() } } $response = $req.GetResponse() try { } finally { if ($response) { $response.Dispose() } } } Write-Progress -Activity "Uploading $fileName..." -Status "Completing upload..." -PercentComplete -1 $req = [System.Net.WebRequest]::CreateHttp("${targetUrl}?multipart=complete&id=$uuid") $req.Method = 'POST' $req.ContentLength = 0 $response = $req.GetResponse() try { } finally { if ($response) { $response.Dispose() } } } finally { if ($sourceStream) { $sourceStream.Dispose() } } } } } #EndRegion '.\public\Assets\New-ProGetAsset.ps1' 111 #Region '.\public\Assets\Publish-ProGetAsset.ps1' -1 function Publish-ProGetAsset { [CmdletBinding()] Param( [Parameter(Mandatory)] [String] $Feed, [Parameter(Mandatory)] [String[]] $File ) end { foreach ($F in $File) { $params = @{ Slug = "/endpoints/$Feed/content/{0}" -f (Split-Path $F -Leaf) Method = 'PUT' File = $F } Invoke-ProGet @params } } } #EndRegion '.\public\Assets\Publish-ProGetAsset.ps1' 25 #Region '.\public\Feeds\Connectors\Get-ProGetConnector.ps1' -1 function Get-ProGetConnector { <# .SYNOPSIS Retrieves information about connectors in ProGet. .DESCRIPTION The `Get-ProGetConnector` function retrieves details about one or more connectors in ProGet. If a connector name is provided, it retrieves information for that specific connector. If no name is provided, it lists all connectors. .PARAMETER Name The name of the connector to retrieve. If not specified, all connectors are listed. .EXAMPLE Get-ProGetConnector Retrieves a list of all connectors in ProGet. .EXAMPLE Get-ProGetConnector -Name "MyConnector" Retrieves details about the connector named "MyConnector". #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Commands/Get-ProGetConnector')] Param( [Parameter()] [String] $Name ) process{ $params = @{ Method = 'GET' } if($Name){ $params.add('Slug',"/api/management/connectors/get/$Name") } else { $params.Add('Slug','/api/management/connectors/list') } Invoke-ProGet @params } } #EndRegion '.\public\Feeds\Connectors\Get-ProGetConnector.ps1' 44 #Region '.\public\Feeds\Connectors\New-ProGetConnector.ps1' -1 function New-ProGetConnector { <# .SYNOPSIS Creates a new connector in ProGet. .DESCRIPTION The `New-ProGetConnector` function allows you to create a new connector in ProGet. Connectors are used to link feeds to external sources, such as NuGet, Chocolatey, Docker, and others. This function supports various feed types and allows configuration of metadata caching, filters, and credentials. .PARAMETER Name The name of the connector. This parameter is mandatory. .PARAMETER FeedType The type of feed the connector is associated with. Supported types include 'universal', 'nuget', 'chocolatey', 'npm', 'maven', 'powershell', 'docker', 'rubygems', 'vsix', 'asset', 'romp', 'pypi', 'helm', 'rpm', 'conda', and 'cran'. This parameter is mandatory. .PARAMETER Url The URL of the external source for the connector. If not specified, a default URL is used based on the feed type. .PARAMETER Timeout The timeout value (in seconds) for the connector. This parameter is mandatory. .PARAMETER Credential A PSCredential object containing the username and password for authentication with the external source. .PARAMETER Filters An array of filters to apply to the connector. .PARAMETER MetadataCacheEnabled Enables metadata caching for the connector. .PARAMETER MetadataCacheMinutes Specifies the duration (in minutes) for which metadata is cached. Defaults to 30 minutes. .PARAMETER MetadataCacheCount Specifies the maximum number of metadata items to cache. Defaults to 100 items. .EXAMPLE New-ProGetConnector -Name "NuGetConnector" -FeedType "nuget" -Timeout 60 Creates a new NuGet connector named "NuGetConnector" with a timeout of 60 seconds. .EXAMPLE New-ProGetConnector -Name "DockerConnector" -FeedType "docker" -Url "https://custom.docker.registry" -Timeout 120 -Credential (Get-Credential) Creates a new Docker connector named "DockerConnector" with a custom URL, a timeout of 120 seconds, and authentication credentials. .EXAMPLE New-ProGetConnector -Name "FilteredConnector" -FeedType "npm" -Filters @("filter1", "filter2") -MetadataCacheEnabled Creates a new NPM connector named "FilteredConnector" with specified filters and metadata caching enabled. #> [CmdletBinding()] Param( [Parameter(Mandatory)] [String] $Name, [Parameter(Mandatory)] [ValidateSet('universal','nuget','chocolatey','npm','maven','powershell','docker','rubygems','vsix','asset','romp','pypi','helm','rpm','conda','cran')] [String] $FeedType, [Parameter()] [String] $Url, [Parameter(Mandatory)] [Int] $Timeout, [Parameter()] [System.Management.Automation.PSCredential] $Credential, [Parameter()] [String[]] $Filters, [Parameter()] [Switch] $MetadataCacheEnabled, [Parameter()] [Int] $MetadataCacheMinutes = 30, #Hard default in ProGet, default value is ignored by this module code due to PSBoundParameters [Parameter()] [Int] $MetadataCacheCount = 100 #Hard default in ProGet, default value is ignored by this module code due to PSBoundParameters ) process { $feedUrlDefaults = @{ universal = $null nuget = 'https://api.nuget.org/v3/index.json' chocolatey = 'https://community.chocolatey.org/api/v2' npm = 'https://registry.npmjs.org' maven = 'https://repo1.maven.org/maven2' bower = 'https://registry.bower.io/packages' docker = 'https://registry.hub.docker.com' debian = 'http://ftp.debian.org/debian/' pypi = 'https://pypi.org' conda = 'https://repo.anaconda.com/pkgs/main/' cran = 'https://cran.r-project.org/' } $body = @{} if(-not $url){ $body.Add('url',$feedUrlDefaults["$FeedType"]) #if they pass one, the url will get updated in the enumeration below, so this is fine. } $PSBoundParameters.GetEnumerator() | ForEach-Object { if($_.Key -eq 'Credential'){ $tempCred = $_.Value $body.Add('username',$tempCred.Username) $body.Add('password',$tempCred.GetNetworkCredential().Password) } else { $body.Add($_.Key,$_.Value) } } $params = @{ Slug = '/api/management/connectors/create' Method = 'POST' Body = $body } Invoke-Proget @params } } #EndRegion '.\public\Feeds\Connectors\New-ProGetConnector.ps1' 133 #Region '.\public\Feeds\Connectors\Remove-ProGetConnector.ps1' -1 function Remove-ProGetConnector { <# .SYNOPSIS Removes a connector from ProGet. .DESCRIPTION The `Remove-ProGetConnector` function deletes a specified connector from ProGet. It supports confirmation prompts to prevent accidental deletions and can be forced to skip confirmation. The function uses the ProGet API to perform the deletion. .PARAMETER Connector The name of the connector(s) to remove. This parameter is mandatory and supports pipeline input. .PARAMETER Force Skips the confirmation prompt and forces the removal of the connector(s). .EXAMPLE Remove-ProGetConnector -Connector "MyConnector" Removes the connector named "MyConnector" after confirmation. .EXAMPLE Remove-ProGetConnector -Connector "MyConnector" -Force Forces the removal of the connector named "MyConnector" without confirmation. .EXAMPLE Get-ProGetConnector | Remove-ProGetConnector Pipes the output of `Get-ProGetConnector` to remove all listed connectors after confirmation. #> [CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess)] Param( [Parameter(Mandatory)] [Alias('Name')] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # Call Get-ProGetFeed and parse its output to get feed names $connectors = (Get-ProGetConnector).name if ($wordToComplete) { $connectors.Where{ $_ -match "^$WordToComplete" } } else { $connectors } })] [String[]] $Connector, [Parameter()] [Switch] $Force ) $Connector | ForEach-Object { if ($Force -and -not $Confirm) { $ConfirmPreference = 'None' if ($PSCmdlet.ShouldProcess("$_", 'Remove Connector')) { $params = @{ Slug = "/api/management/connectors/delete/$_" Method = 'DELETE' } Invoke-ProGet @params } } else { if ($PSCmdlet.ShouldProcess("$_", 'Remove Connector')) { $params = @{ Slug = "/api/management/connectors/delete/$_" Method = 'DELETE' } } Invoke-ProGet @params } } } #EndRegion '.\public\Feeds\Connectors\Remove-ProGetConnector.ps1' 81 #Region '.\public\Feeds\Get-ProGetFeed.ps1' -1 function Get-ProGetFeed { <# .SYNOPSIS Returns information about the available feeds in your ProGet instance .DESCRIPTION Supports returning all feeds, feeds by type, or by name .PARAMETER Feed The feed to return .PARAMETER Type The type of feed to return .EXAMPLE Get-ProGetFeed Return all feeds .EXAMPLE Get-ProGetFeed -Type nuget Return all nuget feeds .EXAMPLE Get-ProGetFeed -Feed ChocolateyPackages Return the ChocolateyPackages feed .NOTES #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Get-ProGetFeed')] Param( [Parameter()] [String] $Feed, [Parameter()] [ValidateSet('asset', 'bower', 'conda', 'chocolatey', 'debianlegacy', 'debian', 'docker', 'helm', 'maven', 'npm', 'nuget', 'powershell', 'universal', 'pypi', 'romp', 'rpm', 'rubygems', 'vsix')] [String] $Type ) end { if($Feed -and $Type){ throw 'Only one parameter of Feed or Type can be used at a time' } $params = @{ Method = 'GET' } if ($Feed) { $params['Slug'] = '/api/management/feeds/get/{0}' -f $Feed } else { $params['Slug'] = '/api/management/feeds/list' } $result = Invoke-ProGet @params if ($Type) { $filter = { $_.feedType -eq $Type } $result | Where-Object $filter } else { $result } } } #EndRegion '.\public\Feeds\Get-ProGetFeed.ps1' 71 #Region '.\public\Feeds\New-ProGetFeed.ps1' -1 function New-ProGetFeed { <# .SYNOPSIS Creates a new feed in ProGet. .DESCRIPTION The `New-ProGetFeed` function allows you to create a new feed in ProGet with various configurable options, such as feed type, connectors, retention rules, and more. It supports multiple feed types, including NuGet, Chocolatey, Docker, and others. .PARAMETER Name The name of the feed to create. This parameter is mandatory. .PARAMETER AlternateNames Alternate names for the feed. .PARAMETER Type The type of the feed. Supported types include 'asset', 'bower', 'conda', 'chocolatey', 'debianlegacy', 'debian', 'docker', 'helm', 'maven', 'npm', 'nuget', 'powershell', 'universal', 'pypi', 'romp', 'rpm', 'rubygems', and 'vsix'. Defaults to 'NuGet'. .PARAMETER Active Specifies whether the feed is active. .PARAMETER CacheConnectors Specifies whether connectors should be cached. .PARAMETER SymbolServerEnabled Enables the symbol server for the feed. .PARAMETER StripSymbols Specifies whether symbols should be stripped from packages. .PARAMETER StripSource Specifies whether source files should be stripped from packages. .PARAMETER EndpointUrl The endpoint URL for the feed. .PARAMETER Connectors A list of connectors to associate with the feed. .PARAMETER RetentionRules A list of retention rules to apply to the feed. .PARAMETER Variables A hashtable of variables to associate with the feed. .PARAMETER CanPublish Specifies whether publishing to the feed is allowed. .PARAMETER PackageStatisticsEnabled Enables package statistics for the feed. .PARAMETER RestrictPackageStatistics Restricts access to package statistics. .PARAMETER DeploymentRecordsEnabled Enables deployment records for the feed. .PARAMETER UsageRecordsEnabled Enables usage records for the feed. .PARAMETER VulnerabilitiesEnabled Enables vulnerability tracking for the feed. .PARAMETER LicensesEnabled Enables license tracking for the feed. .PARAMETER UseWithProjects Specifies whether the feed can be used with projects. .EXAMPLE New-ProGetFeed -Name "MyFeed" -Type "nuget" -Active -CanPublish Creates a new NuGet feed named "MyFeed" that is active and allows publishing. .EXAMPLE New-ProGetFeed -Name "DockerFeed" -Type "docker" -Connectors @("Connector1", "Connector2") -RetentionRules @("Rule1", "Rule2") Creates a new Docker feed named "DockerFeed" with specified connectors and retention rules. #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/New-ProGetFeed')] Param( [Parameter(Mandatory)] [String] $Name, [Parameter()] [String] $AlternateNames, [Parameter()] [ValidateSet('asset', 'bower', 'conda', 'chocolatey', 'debianlegacy', 'debian', 'docker', 'helm', 'maven', 'npm', 'nuget', 'powershell', 'universal', 'pypi', 'romp', 'rpm', 'rubygems', 'vsix')] [String] $Type = 'NuGet', [Parameter()] [Switch] $Active, [Parameter()] [Switch] $CacheConnectors, [Parameter()] [Switch] $SymbolServerEnabled, [Parameter()] [Switch] $StripSymbols, [Parameter()] [Switch] $StripSource, [Parameter()] [String] $EndpointUrl, [Parameter()] [String[]] $Connectors, [Parameter()] [String[]] $RetentionRules, [Parameter()] [Hashtable] $Variables, [Parameter()] [Switch] $CanPublish, [Parameter()] [Switch] $PackageStatisticsEnabled, [Parameter()] [Switch] $RestrictPackageStatistics, [Parameter()] [Switch] $DeploymentRecordsEnabled, [Parameter()] [Switch] $UsageRecordsEnabled, [Parameter()] [Switch] $VulnerabilitiesEnabled, [Parameter()] [Switch] $LicensesEnabled, [Parameter()] [Switch] $UseWithProjects ) end { $params = @{ Slug = '/api/management/feeds/create' Method = 'POST' Body = @{ name = $Name alternateNames = $AlternateNames feedType = $Type active = $Active.IsPresent cacheConnectors = $CacheConnectors.IsPresent symbolServerEnabled = $SymbolServerEnabled.IsPresent stripSymbols = $StripSymbols.IsPresent stripsource = $StripSource.IsPresent endpointUrl = $EndpointUrl connectors = $Connectors retentionRules = $RetentionRules variables = $Variables canPublish = $CanPublish.IsPresent packageStatisticsEnabled = $PackageStatisticsEnabled.IsPresent restrictPackageStatistics = $RestrictPackageStatistics.IsPresent deploymentRecordsEnabled = $DeploymentRecordsEnabled.IsPresent usageRecordsEnabled = $UsageRecordsEnabled.IsPresent vulnerabilitiesEnabled = $VulnerabilitiesEnabled.IsPresent licenseEnabled = $LicensesEnabled.IsPresent useWithProjects = $UseWithProjects.IsPresent } } if ($Type -eq 'chocolatey') { $params.Body.Add('useApiV3', $True) } Invoke-ProGet @params } } #EndRegion '.\public\Feeds\New-ProGetFeed.ps1' 198 #Region '.\public\Feeds\New-ProGetFeedDropPath.ps1' -1 function New-ProGetFeedDropPath { <# .SYNOPSIS Creates or updates the drop path for a ProGet feed. .DESCRIPTION The `New-ProGetFeedDropPath` function allows you to set or update the drop path for a specified ProGet feed. The drop path is used to specify a directory where packages can be dropped for processing by the feed. .PARAMETER Feed The name of the feed for which the drop path is being set. This parameter is mandatory. .PARAMETER DropPath The directory path to set as the drop path for the feed. If not specified, the drop path will be cleared. .EXAMPLE New-ProGetFeedDropPath -Feed "MyFeed" -DropPath "C:\Packages\Drop" Sets the drop path for the feed "MyFeed" to "C:\Packages\Drop". .EXAMPLE New-ProGetFeedDropPath -Feed "MyFeed" Drop Path will default to C:\Drop\MyFeed. #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/New-ProGetFeedDropPath')] Param( [Parameter(Mandatory)] [String] $Feed, [Parameter()] [String] $DropPath ) $dropArgs = @{ Feed = $Feed } if ($DropPath) { $dropArgs.Add('DropPath', $DropPath) } Invoke-NewDropPathStoredProc @dropArgs } #EndRegion '.\public\Feeds\New-ProGetFeedDropPath.ps1' 48 #Region '.\public\Feeds\Remove-ProGetFeed.ps1' -1 function Remove-ProGetFeed { <# .SYNOPSIS Removes a feed from ProGet. .DESCRIPTION The `Remove-ProGetFeed` function deletes a specified feed from ProGet. It supports pipeline input and provides confirmation prompts to prevent accidental deletions. The function uses the ProGet API to perform the deletion. .PARAMETER Feed The name of the feed(s) to remove. This parameter is mandatory and supports pipeline input. .PARAMETER Force Skips the confirmation prompt and forces the removal of the feed(s). .EXAMPLE Remove-ProGetFeed -Feed "MyFeed" Removes the feed named "MyFeed" after confirmation. .EXAMPLE Remove-ProGetFeed -Feed "MyFeed" -Force Forces the removal of the feed named "MyFeed" without confirmation. .EXAMPLE Get-ProGetFeed | Remove-ProGetFeed Pipes the output of `Get-ProGetFeed` to remove all listed feeds after confirmation. #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Remove-ProGetFeed', ConfirmImpact = 'High', SupportsShouldProcess)] Param( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [Alias('Name')] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # Call Get-ProGetFeed and parse its output to get feed names $feeds = (Get-ProGetFeed).name if ($wordToComplete) { $feeds.Where{ $_ -match "^$WordToComplete" } } else { $feeds } })] [String[]] $Feed, [Parameter()] [Switch] $Force ) process { $Feed | Foreach-Object { if($Force -and -not $Confirm){ $ConfirmPreference = 'None' if($PSCmdlet.ShouldProcess("$_",'Remove Feed')){ $params = @{ Slug = "/api/management/feeds/delete/$_" Method = 'DELETE' } Invoke-ProGet @params } } else { if($PSCmdlet.ShouldProcess("$_", 'Remove Feed')){ $params = @{ Slug = "/api/management/feeds/delete/$_" Method = 'DELETE' } Invoke-ProGet @params } } } } } #EndRegion '.\public\Feeds\Remove-ProGetFeed.ps1' 82 #Region '.\public\ModuleConfiguration\Get-ProGetConfiguration.ps1' -1 function Get-ProGetConfiguration { <# .SYNOPSIS Retrieves the configuration for connecting to ProGet. .DESCRIPTION The `Get-ProGetConfiguration` function retrieves the configuration for ProGet. By default, it retrieves the configuration for ProGet unless a different configuration name is provided. .PARAMETER Configuration The name of the configuration to retrieve. Defaults to 'ProGet'. .EXAMPLE Get-ProGetConfiguration Retrieves the configuration for ProGet. .EXAMPLE Get-ProGetConfiguration -Configuration "CustomConfig" Retrieves the configuration for the configuration named "CustomConfige". #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Get-ProGetConfiguration')] Param( [Parameter()] [Alias('Name')] [String] $Configuration = 'ProGet' ) end { Import-Configuration -CompanyName $env:USERNAME -Name $Configuration } } #EndRegion '.\public\ModuleConfiguration\Get-ProGetConfiguration.ps1' 34 #Region '.\public\ModuleConfiguration\Set-ProGetConfiguration.ps1' -1 function Set-ProGetConfiguration { <# .SYNOPSIS Sets the configuration for connecting to ProGet. .DESCRIPTION The `Set-ProGetConfiguration` function allows you to configure the connection settings for ProGet, including hostname, credentials, ports, and SSL options. The configuration can be saved with a custom name for later use. .PARAMETER Hostname The hostname of the ProGet server. This parameter is mandatory. .PARAMETER Credential A PSCredential object containing the username and password for authenticating with the ProGet server. This parameter is mandatory. .PARAMETER NonSslPort The port to use for non-SSL connections. Defaults to 8624. .PARAMETER UseSSL Specifies whether to use SSL for the connection. This parameter is part of the 'ssl' parameter set. .PARAMETER SslPort The port to use for SSL connections. Defaults to 443. This parameter is mandatory when `UseSSL` is specified. .PARAMETER Name The name of the configuration to save. Defaults to 'ProGet'. .PARAMETER ApiKey The API key to use for authentication. Defaults to 'SetMe'. .EXAMPLE Set-ProGetConfiguration -Hostname "proget.example.com" -Credential (Get-Credential) Sets the configuration for ProGet with the specified hostname and credentials. .EXAMPLE Set-ProGetConfiguration -Hostname "proget.example.com" -ApiKey asdf8675309 Sets the configuration for ProGet with the specified hostname and apikey .EXAMPLE Set-ProGetConfiguration -Hostname "proget.example.com" -ApiKey asdf8675309 -Credential (Get-Credential) Sets the configuration for ProGet with the specified hostname, credential, and apikey .EXAMPLE Set-ProGetConfiguration -Hostname "proget.example.com" -Credential (Get-Credential) -UseSSL -SslPort 8443 Sets the configuration for ProGet with SSL enabled and a custom SSL port. .EXAMPLE Set-ProGetConfiguration -Hostname "proget.example.com" -Credential (Get-Credential) -Name "CustomConfig" Sets the configuration for ProGet with a custom configuration name. #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Set-ProGetConfiguration' , DefaultParameterSetName = 'Apikey')] Param( [Parameter(Mandatory)] [String] $Hostname, [Parameter(Mandatory, ParameterSetName = 'Credential')] [Parameter(Mandatory, ParameterSetName = 'Both')] [System.Management.Automation.PSCredential] $Credential, [Parameter()] [Int] $NonSslPort = '8624', [Parameter()] [Switch] $UseSSL, [Parameter()] [Int] $SslPort = 443, [Parameter()] [String] $Name = 'ProGet', [Parameter(Mandatory, ParameterSetName = 'Apikey')] [Parameter(Mandatory, ParameterSetName = 'Both')] [String] $ApiKey ) end { $Configuration = @{ Hostname = $Hostname NonSslPort = $NonSslPort } if ($UseSSL) { $Configuration.Add('UseSSL', $UseSSL) $Configuration.Add('SSLPort', $SslPort) } switch ($PSCmdlet.ParameterSetName) { 'Credential' { $Configuration.Add('Credential', $Credential) } 'ApiKey' { $Configuration.Add('ApiKey', $([PSCredential]::new('null', ($ApiKey | ConvertTo-SecureString -AsPlainText -Force)))) } 'Both' { if (-not $Credential -or -not $ApiKey) { throw "Both Credential and ApiKey must be provided when using the 'Both' parameter set." } $Configuration.Add('Credential', $Credential) $Configuration.Add('ApiKey', $([PSCredential]::new('null', ($ApiKey | ConvertTo-SecureString -AsPlainText -Force)))) } } $Configuration | Export-Configuration -CompanyName $env:USERNAME -Name $Name -Scope User } } #EndRegion '.\public\ModuleConfiguration\Set-ProGetConfiguration.ps1' 122 #Region '.\public\Security\Groups\New-ProGetGroup.ps1' -1 function New-ProGetGroup { <# .SYNOPSIS Create a new user group in ProGet .PARAMETER Name The group to create .EXAMPLE New-ProGetGroup -Name SomeGroup #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/New-ProGetGroup')] Param( [Parameter(Mandatory)] [String] $Name ) end { Invoke-CreateGroupStoredProc -Name $Name } } #EndRegion '.\public\Security\Groups\New-ProGetGroup.ps1' 22 #Region '.\public\Security\Tasks\Get-ProGetTask.ps1' -1 function Get-ProGetTask { <# .SYNOPSIS Retrieves tasks from ProGet. .DESCRIPTION The `Get-ProGetTask` function retrieves a list of tasks from ProGet by invoking the corresponding stored procedure. .EXAMPLE Get-ProGetTask Retrieves all tasks from ProGet. #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Get-ProGetTask')] Param() end { Invoke-GetTaskStoredProc } } #EndRegion '.\public\Security\Tasks\Get-ProGetTask.ps1' 21 #Region '.\public\Security\Users\Get-ProGetUser.ps1' -1 function Get-ProGetUser { <# .SYNOPSIS Returns user account data from ProGet .PARAMETER Username The username to return .EXAMPLE Get-ProGetUser Return data for all defined users .EXAMPLE Get-ProGetUser -Username bob Return data for user bob #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Get-ProGetUser')] Param( [Parameter()] [String] $Username ) end { Invoke-GetUserStoredProc @PSBoundParameters } } #EndRegion '.\public\Security\Users\Get-ProGetUser.ps1' 30 #Region '.\public\Security\Users\New-ProGetUser.ps1' -1 function New-ProGetUser { <# .SYNOPSIS Create a new ProGet user .DESCRIPTION Create a new ProGet user with the provided properties .PARAMETER Credential The credential object will set the username and password for the user .PARAMETER DisplayName The friendly name of the user account .PARAMETER EmailAddress The email address .PARAMETER Group Any groups the user should be included in .EXAMPLE New-ProGetUser -Credential (Get-Credential) -DisplayName 'Bobby Tables' -Group Users,Chocolatey .EXAMPLE New-ProGetUser -Credential $cred -DisplayName 'Jim Thome' -EmailAddress jim@fabrikam.com -Group Administrators #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/New-ProGetUser')] Param( [Parameter(Mandatory)] [PSCredential] $Credential, [Parameter(Mandatory)] [String] $DisplayName, [Parameter()] [string] $EmailAddress, [Parameter(Mandatory)] [String[]] $Group ) process { $groupXml = ConvertTo-XmlString -Group $Group $params = @{ User_Name = $Credential.UserName Display_Name = $DisplayName Groups_Xml = $groupXml } if($EmailAddress){ $params.add('Email_Address',$EmailAddress) } Invoke-NewUserStoredProc -Params $params Set-ProGetUserPassword -Credential $Credential } } #EndRegion '.\public\Security\Users\New-ProGetUser.ps1' 66 #Region '.\public\Security\Users\Set-ProGetUserPassword.ps1' -1 function Set-ProGetUserPassword { <# .SYNOPSIS Sets the password for a ProGet user account .PARAMETER Credential The credential object that contains the username and updated password for the account you wish to update .EXAMPLE Set-ProGetUserPassword -Credential (Get-Credential) Pass a PSCredential object with the username and new password and the user account will be updated. #> [Cmdletbinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Set-ProGetUserPassword')] Param( [Parameter(Mandatory)] [Alias('Username')] [PSCredential] $Credential ) end { $iterations = 10000 $saltLength = 10 $hashLength = 20 $rfc2898 = [System.Security.Cryptography.Rfc2898DeriveBytes]::new($Credential.GetNetworkCredential().Password, $saltLength, $iterations) $passwordBytes = $rfc2898.GetBytes($hashLength) $saltBytes = $rfc2898.Salt $Params = @{ User_Name = $Credential.UserName Password_Bytes = $passwordBytes Salt_Bytes = $saltBytes } Invoke-UserPasswordStoredProc -Params $Params } } #EndRegion '.\public\Security\Users\Set-ProGetUserPassword.ps1' 43 #Region '.\public\Utility\Merge-Directory.ps1' -1 function Merge-Directory { <# .SYNOPSIS Merges the contents of a directory into a single flat directory. .DESCRIPTION The `Merge-Directory` function flattens the structure of a specified directory by moving all files from subdirectories into the root of the specified directory. It supports confirmation prompts and can be forced to skip confirmation. .PARAMETER Directory The path to the directory to merge. This parameter is mandatory. .PARAMETER Force Skips the confirmation prompt and forces the merge operation. .EXAMPLE Merge-Directory -Directory "C:\MyFolder" Flattens the structure of "C:\MyFolder" by moving all files from subdirectories into the root of "C:\MyFolder" with confirmation. .EXAMPLE Merge-Directory -Directory "C:\MyFolder" -Force Flattens the structure of "C:\MyFolder" by moving all files from subdirectories into the root of "C:\MyFolder" without confirmation. #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Merge-Directory', ConfirmImpact = 'High', SupportsShouldProcess)] Param( [Parameter(Mandatory)] [ValidateScript({ Test-Path $_ })] [String] $Directory, [Parameter()] [Switch] $Force ) end { if ($Force -and -not $Confirm) { $ConfirmPreference = 'None' if ($PSCmdlet.ShouldProcess("$_", 'Merge (flatten) Directory')) { Push-Location $Directory Get-ChildItem -Recurse -File | ForEach-Object { Move-Item $_.FullName $pwd } Pop-Location } } else { if ($PSCmdlet.ShouldProcess("$_", 'Merge (flatten) Directory')) { Push-Location $Directory Get-ChildItem -Recurse -File | ForEach-Object { Move-Item $_.FullName $pwd } Pop-Location } } } } #EndRegion '.\public\Utility\Merge-Directory.ps1' 59 #Region '.\public\Utility\Set-ProGetSSLConfig.ps1' -1 function Set-ProGetSslConfig { <# .SYNOPSIS Updates the ProGet configuration file and restarts the ProGet services. .DESCRIPTION This script updates the ProGet configuration file with specified parameters, handles SSL certificate settings, and restarts the ProGet services. It processes parameters, updates the XML configuration file, and ensures the correct attributes are set. .PARAMETER ConfigFile Specifies the path to the ProGet configuration file. Default is 'C:\ProgramData\Inedo\SharedConfig\ProGet.config'. .PARAMETER Location Specifies the location of the certificate store. Valid values are 'User' and 'LocalMachine'. .PARAMETER Store Specifies the certificate store. Valid values are 'My', and 'Root'. .PARAMETER Subject Specifies the subject name of the certificate. .PARAMETER AllowInvalid A switch parameter that allows invalid certificates if specified. .PARAMETER CertPassword Specifies the password for the certificate. .PARAMETER Urls Specifies the URLs for the web server to bind too. .EXAMPLE Set-ProGetSslConfig -Location 'Machine' -Store 'My' -Subject 'example.com' -AllowInvalid -CertPassword 'password' -Urls 'http://*:8624/' This command updates the ProGet configuration to use a certificate with the subject 'example.com' from the specified Windows certificate store. .EXAMPLE Set-ProGetSslConfig -CertFile "C:\proget_cert\cert.pfx" -CertPassword ('poshacme' | ConvertTo-SecureString -AsPlainText -Force) -Urls http://*:8624/,https://*:8443/ Updates ProGet configuration to use the provided pfx file and pfx password to secure the ProGet instance. Also allows for non-http binding to port 8624 .EXAMPLE Set-ProGetSslConfig -CertFile "C:\proget_cert\cert.pem" -KeyFile "C:\proget_cert\cert.key" -Urls http://*:8624/,https://*:8443/ Updates ProGet configuration to use the provided pem and key file to secure the ProGet instance with SSL .NOTES #> [CmdletBinding(HelpUri = 'https://steviecoaster.github.io/Pagootle/Commands/Set-ProGetSslConfig')] Param( [Parameter(ParameterSetName = 'WindowsStore')] [Parameter(ParameterSetName = 'CertFile')] [Parameter(ParameterSetName = 'Pfx')] [String] $ConfigFile = 'C:\ProgramData\Inedo\SharedConfig\ProGet.config', [Parameter(Mandatory, ParameterSetName = 'WindowsStore')] [ValidateSet('User', 'LocalMachine')] [String] $Location, [Parameter(Mandatory, ParameterSetName = 'WindowsStore')] [ValidateSet('TrustedPeople', 'My', 'WebHosting')] [String] $Store, [Parameter(Mandatory, ParameterSetName = 'WindowsStore')] [String] $Subject, [Parameter(ParameterSetName = 'WindowsStore')] [Switch] $AllowInvalid, [Parameter(Mandatory, ParameterSetName = 'CertFile')] [Parameter(Mandatory,ParameterSetName = 'Pfx')] [ValidateScript({ Test-Path $_ })] [String] $CertFile, [Parameter(ParameterSetName = 'Pfx')] [SecureString] $CertPassword, [Parameter(ParameterSetName = 'CertFile')] [ValidateScript({ Test-Path $_ })] [String] $KeyFile, [Parameter(ParameterSetName = 'CertFile')] [Parameter(ParameterSetName = 'Pfx')] [Parameter(ParameterSetName = 'WindowsStore')] [ValidateScript({ $_ | Foreach-object { if($psitem.EndsWith('/')){ $true } else { throw "Url must end with a trailing '/'!" } } })] [String[]] $Urls = 'http://*:8624/' ) end { #Update the configuration file [xml]$xml = Get-Content $ConfigFile $webServerNode = $xml.InedoAppConfig.WebServer #Set some defaults $webServerNode.attributes.RemoveAll() $webServerNode.SetAttribute('Enabled',$true) switch($PSCmdlet.ParameterSetName){ 'WindowsStore' { $PSBoundParameters.GetEnumerator() | ForEach-Object { if ($_.Key -eq 'AllowInvalid') { $webServerNode.SetAttribute($($_.Key), $([bool]$_.Value)) } else { $webServerNode.SetAttribute($_.Key, $_.Value) } } if (-not $PSBoundParameters.ContainsKey('AllowInvalid')) { $webServerNode.SetAttribute('AllowInvalid', $false) } else { $webServerNode.SetAttribute('AllowInvalid',$AllowInvalid) } $certificateThumbprint = (Get-ChildItem Cert:\$($Location)\$($Store) | Where-Object {$_.Subject -like "CN=$Subject"}).Thumbprint $ServiceUser = (Get-CimInstance Win32_Service -Filter "Name = 'INEDOPROGETWEBSVC'").StartName Set-CertPermissions -Thumbprint $certificateThumbprint -Location $Location -Store $Store -ServiceUser $ServiceUser } 'CertFile' { $PSBoundParameters.GetEnumerator() | ForEach-Object { if(-not ($_.Key -eq 'ConfigFile')){ $webServerNode.SetAttribute($_.Key,$_.Value) } } } 'Pfx' { $PSBoundParameters.GetEnumerator() | ForEach-Object { if(-not ($_.Key -eq 'ConfigFile')){ if($_.Key -eq 'CertPassword'){ $tempCred = [System.Management.Automation.PSCredential]::new('toss',$CertPassword) $CleartextPassword = $tempCred.GetNetworkCredential().Password $webServerNode.SetAttribute('Password',$CleartextPassword) } else { $webServerNode.SetAttribute($_.Key,$_.Value) } } } if($webServerNode.HasAttribute('KeyFile')){ $webServerNode.RemoveAttribute('KeyFile') } } } #Handle port bindings if($urls){ if($Urls.Count -gt 1){ $webServerNode.SetAttribute('Urls',$($Urls -join ';')) } else { $webServerNode.SetAttribute('Urls',$Urls) } } #Write the config file $xml.Save($ConfigFile) #Update the permissions on the certificate private key, to give service user READ permissions #Restart the ProGet services Get-Service inedoproget* | Restart-Service } } #EndRegion '.\public\Utility\Set-ProGetSSLConfig.ps1' 189 #Region '.\Suffix.ps1' -1 #EndRegion '.\Suffix.ps1' 1 |