├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── SECURITY.md ├── .gitignore ├── .readthedocs.yaml ├── .vscode ├── extensions.json ├── settings.json └── tasks.json ├── CloudFormation ├── Manual │ ├── PSGELFunctions.yml │ ├── PSGalleryPipeline.yml │ └── PowerShellCodeBuildGit.yml └── PSGalleryExplorer │ ├── ChildTemplates │ ├── PSGEAlarms.yml │ ├── PSGES3Buckets.yml │ └── PSGESSM.yml │ ├── ControlPlane-Parameters │ └── PSGalleryExplorer.json │ └── PSGalleryExplorer-ControlPlane.yml ├── CodeBuild ├── IntegrationTest │ ├── New-IntegrationInfrastructure.ps1 │ ├── Remove-IntegrationInfrastructure.ps1 │ ├── Tests │ │ └── Integration.Tests.ps1 │ └── buildspec.yml ├── UnitTestAndBuild │ ├── Publish-CFNTemplatesToS3.ps1 │ ├── Validate-CFNTemplates.Tests.ps1 │ ├── Validate-JSONConfigurations.Tests.ps1 │ ├── buildspec.yml │ ├── dotnet-install.ps1 │ └── dotnet-install.sh ├── configure_aws_credential.ps1 └── install_modules.ps1 ├── LICENSE ├── README.md ├── actions_bootstrap.ps1 ├── appveyor.yml ├── buildspec_powershell_windows.yml ├── buildspec_pwsh_linux.yml ├── buildspec_pwsh_windows.yml ├── configure_aws_credential.ps1 ├── docs ├── CHANGELOG.md ├── Find-ModuleByCommand.md ├── Find-PSGModule.md ├── PSGalleryExplorer-Data_Collection.md ├── PSGalleryExplorer-FAQ.md ├── PSGalleryExplorer-Metrics.md ├── PSGalleryExplorer.md ├── assets │ ├── PSGalleryExplorer.png │ ├── PSGalleryExplorerIcon.png │ ├── PSGalleryExplorer_datapull.png │ ├── PSGalleryExplorer_favicon-32x32.png │ ├── Serverless_PowerShell_DataPull.png │ ├── diagrams │ │ └── PSGalleryExplorer_datapull.drawio │ ├── favicon_io │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon.ico │ │ └── site.webmanifest │ └── psgalleryexplorer.gif ├── index.md └── requirements.txt ├── install_modules.ps1 ├── lambdafunctions └── PowerShell │ └── PubXMLMonitor │ ├── PubXMLMonitor.build.ps1 │ ├── PubXMLMonitor.ps1 │ └── PubXMLMonitor.settings.ps1 ├── mkdocs.yml └── src ├── PSGalleryExplorer.Settings.ps1 ├── PSGalleryExplorer.build.ps1 ├── PSGalleryExplorer ├── Imports.ps1 ├── PSGalleryExplorer.Format.ps1xml ├── PSGalleryExplorer.psd1 ├── PSGalleryExplorer.psm1 ├── Private │ ├── Confirm-DataLocation.ps1 │ ├── Confirm-MetadataUpdate.ps1 │ ├── Confirm-XMLDataSet.ps1 │ ├── Expand-XMLDataSet.ps1 │ ├── Get-RemoteFile.ps1 │ ├── Import-XMLDataSet.ps1 │ └── Invoke-XMLDataCheck.ps1 └── Public │ ├── Find-ModuleByCommand.ps1 │ └── Find-PSGModule.ps1 ├── PSScriptAnalyzerSettings.psd1 └── Tests ├── Integration └── PSGalleryExplorer.Tests.ps1 └── Unit ├── ExportedFunctions.Tests.ps1 ├── PSGalleryExplorer-Module.Tests.ps1 ├── Private ├── Confirm-DataLocation.Tests.ps1 ├── Confirm-MetadataUpdate.Tests.ps1 ├── Confirm-XMLDataSet.Tests.ps1 ├── Expand-XMLDataSet.Tests.ps1 ├── Get-RemoteFile.Tests.ps1 ├── Import-XMLDataSet.Tests.ps1 └── Invoke-XMLDataCheck.Tests.ps1 └── Public ├── Find-ModuleByCommand.Tests.ps1 └── Find-PSGModule.Tests.ps1 /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at **[EMAIL REQUIRED]**. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for your interest in contributing to the **PSGalleryExplorer Project**. 4 | 5 | Whether it's a bug report, new feature, correction, or additional documentation, your feedback and contributions are appreciated. 6 | 7 | Please read through this document before submitting any issues or pull requests to ensure all the necessary information is provided to effectively respond to your bug report or contribution. 8 | 9 | Please note there is a code of conduct, please follow it in all your interactions with the project. 10 | 11 | ## Reporting Bugs/Feature Requests 12 | 13 | When filing an issue, please check [existing open](https://github.com/techthoughts2/PSGalleryExplorer/issues), or [recently closed](https://github.com/techthoughts2/PSGalleryExplorer/issues?q=is%3Aissue+is%3Aclosed), issues to make sure somebody else hasn't already 14 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 15 | 16 | * A reproducible test case or series of steps 17 | * The version of the module being used 18 | * Any modifications you've made relevant to the bug 19 | * Anything unusual about your environment or deployment 20 | 21 | ## Contributing via Pull Requests 22 | 23 | Contributions via pull requests are much appreciated. Before sending a pull request, please ensure that: 24 | 25 | 1. You are working against the latest source on the *Enhancements* branch. 26 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 27 | 3. You open an issue to discuss any significant work - I'd hate for your time to be wasted. 28 | 29 | To send a pull request, please: 30 | 31 | 1. Fork the repository. 32 | 2. Checkout the *Enhancements* branch 33 | 3. Modify the source; please focus on the specific change you are contributing. Please refrain from code styling changes, it will be harder to focus on your change. 34 | 4. Ensure local tests pass. 35 | 5. Commit to your fork using clear commit messages. 36 | 6. Send a pull request, answering any default questions in the pull request interface. 37 | 38 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 39 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 40 | 41 | ## Finding contributions to work on 42 | 43 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (dev/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/techthoughts2/PSGalleryExplorer/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) issues is a great place to start. 44 | 45 | ## Code of Conduct 46 | 47 | This project has a [Code of Conduct](CODE_OF_CONDUCT.md). 48 | 49 | ## Licensing 50 | 51 | See the [LICENSE](../LICENSE) file for our project's licensing. 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Submit a new bug 4 | title: Bug report 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | ### Expected Behavior 13 | 14 | 15 | ### Current Behavior 16 | 17 | 18 | ### Possible Solution 19 | 20 | 21 | ### Steps to Reproduce 22 | 23 | 24 | 25 | 1. 26 | 2. 27 | 3. 28 | 4. 29 | 30 | ### Context (Environment) 31 | 32 | 33 | * Operating System and version as reported by `$PSVersionTable.OS`: 34 | * PowerShell versions as reported by `$PSVersionTable.PSEdition`: 35 | 36 | 37 | 38 | ### Detailed Description 39 | 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: 'Feature request' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Description 11 | 12 | 13 | ### Describe the solution you'd like 14 | 15 | 16 | ### Describe any alternatives you've considered 17 | 18 | 19 | ### Additional context 20 | 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request 2 | 3 | ## Issue 4 | 5 | Issue #, if available: 6 | 7 | ## Description 8 | 9 | Description of changes: 10 | 11 | ## License 12 | 13 | By submitting this pull request, I confirm that my contribution is made under the terms of the projects associated license. 14 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you discover a vulnerability in PSGalleryExplorer, please follow the _following process_: 6 | 7 | 1. Open a generic bug issue advising you have discovered a vulnerability. 8 | - Avoid sharing specifics or details of the vulnerability in an open GitHub issue. 9 | 2. A repo owner will reach out to you to establish a private form of communication. 10 | 3. We will evaluate the vulnerability and, if necessary, release a fix or mitigating steps to address it. We will contact you to let you know the outcome, and will credit you in the report. 11 | 12 | Please **do not disclose the vulnerability publicly** until a fix is released! 13 | 14 | 4. Once we have either a) published a fix, or b) declined to address the vulnerability for whatever reason, you are free to publicly disclose it. 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Archive 2 | Artifacts 3 | cov.xml 4 | coverage.xml 5 | *.bkp 6 | *.dtmp 7 | # OS generated files # 8 | ###################### 9 | .DS_Store 10 | .DS_Store? 11 | ._* 12 | .Spotlight-V100 13 | .Trashes 14 | ehthumbs.db 15 | Thumbs.db 16 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.readthedocs.io/en/stable/config-file/index.html 2 | 3 | # .readthedocs.yaml 4 | # Read the Docs configuration file 5 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 6 | 7 | # Required 8 | version: 2 9 | 10 | build: 11 | os: ubuntu-22.04 12 | tools: 13 | python: "3.11" 14 | 15 | mkdocs: 16 | configuration: mkdocs.yml 17 | 18 | python: 19 | install: 20 | - requirements: docs/requirements.txt 21 | 22 | # # Build PDF & ePub 23 | formats: all 24 | # - epub 25 | # - pdf 26 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-vscode.PowerShell", 6 | "ryanluker.vscode-coverage-gutters", 7 | "DavidAnson.vscode-markdownlint", 8 | "aaron-bond.better-comments", 9 | "aws-scripting-guy.cform", 10 | "amazonwebservices.aws-toolkit-vscode", 11 | "kddejong.vscode-cfn-lint", 12 | "DanielThielking.aws-cloudformation-yaml", 13 | "redhat.vscode-yaml" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "markdownlint.config": { 3 | "default": true, 4 | "MD007": { 5 | "indent": 4 6 | }, 7 | "no-hard-tabs": false 8 | }, 9 | // When enabled, will trim trailing whitespace when you save a file. 10 | "files.trimTrailingWhitespace": true, 11 | // specifies the location of the explicity ScriptAnalyzer settings file 12 | "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1", 13 | // specifies the PowerShell coding style used in this project (https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81) 14 | "powershell.codeFormatting.preset": "Stroustrup", 15 | "cSpell.enableFiletypes": [ 16 | "!yaml", 17 | "powershell" 18 | ], 19 | "cSpell.words": [ 20 | "Allman", 21 | "allmodules", 22 | "amazonlinux", 23 | "APIURL", 24 | "buildspec", 25 | "Catesta", 26 | "Clixml", 27 | "datetime", 28 | "defaultparametersetname", 29 | "discoverability", 30 | "fanout", 31 | "gldata", 32 | "jakemorrison", 33 | "lambdafunctions", 34 | "notcontains", 35 | "notin", 36 | "OTBS", 37 | "Populator", 38 | "psgalleryexplorer", 39 | "PSGE", 40 | "PSGES", 41 | "pwsh", 42 | "ratelimit", 43 | "Ryver", 44 | "Sightview", 45 | "Stroustrup", 46 | "telegramchannel", 47 | "telegramtoken" 48 | ] 49 | } 50 | 51 | -------------------------------------------------------------------------------- /CloudFormation/Manual/PSGELFunctions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: "2010-09-09" 3 | 4 | Description: PSGalleryExplorer S3 Code Bucket for Lambda Functions 5 | 6 | Parameters: 7 | ServiceName: 8 | Type: String 9 | Description: 'Name of the service that will be used to tag each resource.' 10 | Default: PSGalleryExplorer 11 | # The bucket name can be between 3 and 63 characters long, and can contain only lower-case characters, numbers, periods, and dashes. 12 | # Each label in the bucket name must start with a lowercase letter or number. 13 | # The bucket name cannot contain underscores, end with a dash, have consecutive periods, or use dashes adjacent to periods. 14 | # The bucket name cannot be formatted as an IP address (198.51.100.24). 15 | BucketName: 16 | Type: String 17 | Description: Hard coded S3 bucketname 18 | Default: psge 19 | 20 | Resources: 21 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html 22 | PSGELFunctions: 23 | Type: AWS::S3::Bucket 24 | DeletionPolicy: Delete 25 | Properties: 26 | # AccelerateConfiguration: AccelerateConfiguration 27 | # AccessControl: String 28 | # AnalyticsConfigurations: 29 | # - AnalyticsConfiguration 30 | BucketEncryption: 31 | ServerSideEncryptionConfiguration: 32 | - ServerSideEncryptionByDefault: 33 | SSEAlgorithm: AES256 34 | BucketName: !Sub ${BucketName}-psgelfunctions 35 | # CorsConfiguration: 36 | # CorsConfiguration 37 | # InventoryConfigurations: 38 | # - InventoryConfiguration 39 | # LifecycleConfiguration: 40 | # Rules: 41 | # - 42 | # AbortIncompleteMultipartUpload: 43 | # DaysAfterInitiation: 7 44 | # Status: Enabled 45 | # Transitions: 46 | # - 47 | # StorageClass: GLACIER 48 | # TransitionInDays: 30 49 | # LoggingConfiguration: 50 | # DestinationBucketName: !Ref S3BucketLogs 51 | # LogFilePrefix: '/logs/updateapcdata/' 52 | # MetricsConfigurations: 53 | # - MetricsConfiguration 54 | # NotificationConfiguration: 55 | # TopicConfigurations: 56 | # - 57 | # Event: s3:ObjectCreated:* 58 | # Topic: !Ref UpdateSNSTopic 59 | # ObjectLockConfiguration: 60 | # ObjectLockConfiguration 61 | # ObjectLockEnabled: Boolean 62 | PublicAccessBlockConfiguration: 63 | BlockPublicAcls: true 64 | BlockPublicPolicy: true 65 | IgnorePublicAcls: true 66 | RestrictPublicBuckets: true 67 | # ReplicationConfiguration: 68 | # ReplicationConfiguration 69 | # VersioningConfiguration: 70 | # VersioningConfiguration 71 | # WebsiteConfiguration: 72 | # WebsiteConfiguration 73 | Tags: 74 | - Key: Service 75 | Value: !Ref ServiceName 76 | 77 | Outputs: 78 | PSGELFunctionsBucketName: 79 | Value: !Ref PSGELFunctions 80 | Description: Name of the PSGalleryExplorer Amazon S3 bucket that holds Lambda functions 81 | Export: 82 | Name: PSGELFBN 83 | PSGELFunctionsArn: 84 | Description: Arn of the PSGalleryExplorer Amazon S3 bucket that holds Lambda functions 85 | Value: !GetAtt PSGELFunctions.Arn 86 | PSGELFunctionsDomainName: 87 | Description: Domain name of the PSGalleryExplorer Amazon S3 bucket that holds Lambda functions 88 | Value: !GetAtt PSGELFunctions.DomainName 89 | -------------------------------------------------------------------------------- /CloudFormation/PSGalleryExplorer/ChildTemplates/PSGEAlarms.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | 4 | Description: 'AUTOMATED: PSGalleryExplorer - alarms deployment' 5 | 6 | Transform: 'AWS::Serverless-2016-10-31' 7 | 8 | Parameters: 9 | ServiceName: 10 | Type: String 11 | Description: The name of the service being deployed. Used for Developer AWS Account Resource Names. 12 | 13 | LambdaS3BucketName: 14 | Type: String 15 | Description: The S3 Bucket holding the Lambda code 16 | 17 | LMFunctionS3KeyPubXMLMonitor: 18 | Type: String 19 | Description: S3 Key for the PubXMLMonitor Lambda function(s) zip file 20 | 21 | LMFunctionHandlerPubXMLMonitor: 22 | Type: String 23 | Description: PubXMLMonitor Lambda HANDLER provided by New-AWSPowerShellLambdaPackage during build 24 | 25 | ResourceType: 26 | Type: String 27 | Description: Determine the type of resource that will be deployed 28 | AllowedValues: 29 | - core 30 | - dev 31 | - test 32 | - prod 33 | 34 | Resources: 35 | # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html 36 | # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventsource.html 37 | # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchevent.html 38 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html#cfn-events-rule-eventpattern 39 | PubXMLMonitor: 40 | Type: 'AWS::Serverless::Function' 41 | Properties: 42 | # Architectures: x86_64 43 | # AssumeRolePolicyDocument: JSON 44 | # AutoPublishAlias: String 45 | # AutoPublishCodeSha256: String 46 | # CodeSigningConfigArn: String 47 | CodeUri: 48 | Bucket: !Ref LambdaS3BucketName 49 | Key: !Ref LMFunctionS3KeyPubXMLMonitor 50 | # DeadLetterQueue: Map | DeadLetterQueue 51 | # DeploymentPreference: DeploymentPreference 52 | Description: 'Determines age of PubXML and sends to CloudWatch Metric' 53 | Environment: 54 | Variables: 55 | S3_BUCKET_NAME: !ImportValue PubXMLDataBN 56 | SERVICE_NAME: !Ref ServiceName 57 | # EphemeralStorage: EphemeralStorage 58 | # EventInvokeConfig: EventInvokeConfiguration 59 | # Events: EventSource 60 | # FileSystemConfigs: List 61 | # FunctionName: 62 | # FunctionUrlConfig: FunctionUrlConfig 63 | Handler: !Ref LMFunctionHandlerPubXMLMonitor 64 | # ImageConfig: ImageConfig 65 | # ImageUri: String 66 | # InlineCode: String 67 | # KmsKeyArn: String 68 | # Layers: List 69 | MemorySize: 768 70 | # PackageType: String 71 | # PermissionsBoundary: String 72 | Policies: 73 | - AWSLambdaBasicExecutionRole 74 | - CloudWatchPutMetricPolicy: {} 75 | - S3CrudPolicy: 76 | BucketName: !ImportValue PubXMLDataBN 77 | - SSMParameterReadPolicy: 78 | ParameterName: telegramtoken 79 | - SSMParameterReadPolicy: 80 | ParameterName: telegramchannel 81 | # ProvisionedConcurrencyConfig: ProvisionedConcurrencyConfig 82 | # ReservedConcurrentExecutions: Integer 83 | # Role: String 84 | # RolePath: String 85 | Runtime: dotnet6 86 | # RuntimeManagementConfig: RuntimeManagementConfig 87 | # SnapStart: SnapStart 88 | Tags: 89 | ServiceName: !Ref ServiceName 90 | StackName: !Ref AWS::StackName 91 | ResourceType: !Ref ResourceType 92 | Events: 93 | RateSchedule: 94 | Type: Schedule 95 | Properties: 96 | Enabled: true 97 | Schedule: 'rate(1 day)' 98 | Timeout: 60 99 | 100 | #https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html 101 | PubXMLMonitorLogGroup: 102 | Type: AWS::Logs::LogGroup 103 | DependsOn: PubXMLMonitor 104 | DeletionPolicy: Delete 105 | UpdateReplacePolicy: Retain 106 | Properties: 107 | # KmsKeyId: String 108 | LogGroupName: !Sub '/aws/lambda/${PubXMLMonitor}' 109 | RetentionInDays: 14 110 | Tags: 111 | - Key: ServiceName 112 | Value: !Ref ServiceName 113 | - Key: StackName 114 | Value: !Ref AWS::StackName 115 | - Key: ResourceType 116 | Value: !Ref ResourceType 117 | 118 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html 119 | PubXMLMonitorAlarm: 120 | Type: AWS::CloudWatch::Alarm 121 | Properties: 122 | ActionsEnabled: true 123 | AlarmActions: 124 | - !ImportValue AlertSNSTopic 125 | AlarmDescription: Alarms if the PubXML file is more than 8 days old. 126 | AlarmName: PubXMLMonitor 127 | ComparisonOperator: GreaterThanOrEqualToThreshold 128 | # DatapointsToAlarm: Integer 129 | # EvaluateLowSampleCountPercentile: String 130 | Dimensions: 131 | - Name: PubXML 132 | Value: DaysOld 133 | EvaluationPeriods: 1 134 | # ExtendedStatistic: String 135 | # InsufficientDataActions: 136 | # - String 137 | MetricName: PubXMLAge 138 | # Metrics: 139 | # - MetricDataQuery 140 | Namespace: !Ref ServiceName 141 | # OKActions: 142 | # - String 143 | Period: 3600 144 | Statistic: Maximum 145 | Threshold: 8 146 | # ThresholdMetricId: String 147 | TreatMissingData: notBreaching 148 | # Unit: String 149 | 150 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-dashboard.html 151 | PSGEPubXMLMonitorDashboard: 152 | Type: AWS::CloudWatch::Dashboard 153 | Properties: 154 | DashboardName: !Sub '${ServiceName}-PSGEPubXMLMonitorDashboard' 155 | DashboardBody: !Sub | 156 | { 157 | "widgets": [ 158 | { 159 | "height": 10, 160 | "width": 20, 161 | "y": 12, 162 | "x": 0, 163 | "type": "metric", 164 | "properties": { 165 | "metrics": [ 166 | [ 167 | "PSGalleryExplorer", 168 | "PubXMLAge", 169 | "PubXML", 170 | "DaysOld", 171 | { 172 | "region": "us-west-2", 173 | "label": "Data Cache Age (Days)" 174 | } 175 | ] 176 | ], 177 | "view": "timeSeries", 178 | "stacked": false, 179 | "region": "us-west-2", 180 | "period": 86400, 181 | "stat": "Maximum", 182 | "start": "-P90D", 183 | "end": "P0D", 184 | "title": "PSGalleryExplorer Data Cache Age Metric", 185 | "yAxis": { 186 | "left": { 187 | "label": "Days Old", 188 | "min": 0 189 | } 190 | }, 191 | "annotations": { 192 | "horizontal": [ 193 | { 194 | "label": "Untitled annotation", 195 | "value": 8 196 | } 197 | ] 198 | } 199 | } 200 | }, 201 | { 202 | "height": 10, 203 | "width": 20, 204 | "y": 0, 205 | "x": 0, 206 | "type": "metric", 207 | "properties": { 208 | "metrics": [ 209 | [ 210 | "AWS/CloudFront", 211 | "Requests", 212 | "Region", 213 | "Global", 214 | "DistributionId", 215 | "E253EO4S3ELURG", 216 | { 217 | "region": "us-east-1" 218 | } 219 | ] 220 | ], 221 | "view": "timeSeries", 222 | "stacked": false, 223 | "region": "us-west-2", 224 | "period": 86400, 225 | "stat": "Sum", 226 | "start": "-P90D", 227 | "end": "P0D", 228 | "title": "Data Cache Download Requests", 229 | "yAxis": { 230 | "left": { 231 | "label": "Download Count", 232 | "min": 0 233 | } 234 | }, 235 | "setPeriodToTimeRange": true 236 | } 237 | }, 238 | { 239 | "height": 2, 240 | "width": 20, 241 | "y": 10, 242 | "x": 0, 243 | "type": "alarm", 244 | "properties": { 245 | "title": "Over 8 Days Alarm Status", 246 | "alarms": [ 247 | "arn:aws:cloudwatch:us-west-2:699483786831:alarm:PubXMLMonitor" 248 | ] 249 | } 250 | } 251 | ] 252 | } 253 | 254 | Outputs: 255 | PubXMLMonitorARN: 256 | Description: Arn for PubXMLMonitor Lambda 257 | Value: !GetAtt PubXMLMonitor.Arn 258 | Export: 259 | Name: !Sub ${ServiceName}-PubXMLMonitorARN 260 | 261 | PubXMLMonitorAlarmARN: 262 | Description: PubXMLMonitorAlarm Alarm ARN 263 | Value: !GetAtt PubXMLMonitorAlarm.Arn 264 | Export: 265 | Name: !Sub ${ServiceName}-PubXMLMonitorAlarmARN 266 | -------------------------------------------------------------------------------- /CloudFormation/PSGalleryExplorer/ChildTemplates/PSGESSM.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | 4 | Description: 'AUTOMATED: PSGalleryExplorer - SSM deployment' 5 | 6 | Parameters: 7 | ServiceName: 8 | Type: String 9 | Description: The name of the service being deployed. Used for Developer AWS Account Resource Names. 10 | 11 | ResourceType: 12 | Type: String 13 | Description: Determine the type of resource that will be deployed 14 | AllowedValues: 15 | - core 16 | - dev 17 | - test 18 | - prod 19 | 20 | Resources: 21 | 22 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html 23 | PSGEMaintenanceWindowTaskRole: 24 | Type: AWS::IAM::Role 25 | Properties: 26 | RoleName: !Sub '${ServiceName}-PSGEMaintenanceWindowTaskRole' 27 | Tags: 28 | - Key: ServiceName 29 | Value: !Ref ServiceName 30 | - Key: StackName 31 | Value: !Ref AWS::StackName 32 | - Key: ResourceType 33 | Value: !Ref ResourceType 34 | AssumeRolePolicyDocument: 35 | Version: '2012-10-17' 36 | Statement: 37 | - Effect: Allow 38 | Principal: 39 | Service: ssm.amazonaws.com 40 | Action: 41 | - sts:AssumeRole 42 | Path: / 43 | ManagedPolicyArns: 44 | - arn:aws:iam::aws:policy/service-role/AmazonSSMMaintenanceWindowRole 45 | Policies: 46 | - PolicyName: pass-role 47 | PolicyDocument: 48 | Version: '2012-10-17' 49 | Statement: 50 | - Effect: Allow 51 | Action: 52 | - iam:PassRole 53 | Resource: 54 | - !GetAtt PSGESSMMaintenanceWindowTaskCommandRole.Arn 55 | 56 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html 57 | PSGESSMMaintenanceWindowTaskCommandRole: 58 | Type: AWS::IAM::Role 59 | Properties: 60 | RoleName: !Sub '${ServiceName}-PSGESSMMaintenanceWindowTaskCommandRole' 61 | Tags: 62 | - Key: ServiceName 63 | Value: !Ref ServiceName 64 | - Key: StackName 65 | Value: !Ref AWS::StackName 66 | - Key: ResourceType 67 | Value: !Ref ResourceType 68 | AssumeRolePolicyDocument: 69 | Version: '2012-10-17' 70 | Statement: 71 | - Effect: Allow 72 | Principal: 73 | Service: ssm.amazonaws.com 74 | Action: 75 | - sts:AssumeRole 76 | Path: / 77 | # ManagedPolicyArns: 78 | # - arn:${AWS::Partition}:iam::${AWS::Partition}:policy/service-role/AmazonSSMMaintenanceWindowRole 79 | # # - arn:${AWS::Partition}:iam::${AWS::Partition}:policy/service-role/AmazonSSMAutomationRole 80 | # - arn:${AWS::Partition}:iam::${AWS::Partition}:policy/CloudWatchFullAccess 81 | Policies: 82 | - PolicyName: SSMPublishAlertTopic 83 | PolicyDocument: 84 | Version: '2012-10-17' 85 | Statement: 86 | - Effect: Allow 87 | Action: 88 | - sns:Publish 89 | Resource: 90 | - !ImportValue AlertSNSTopic 91 | 92 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-maintenancewindow.html 93 | PSGEMaintWindow: 94 | Type: AWS::SSM::MaintenanceWindow 95 | Properties: 96 | AllowUnassociatedTargets: false 97 | Cutoff: 0 98 | Description: 'Maintenance window to fetch combined XML file' 99 | Duration: 1 100 | # EndDate: String 101 | Name: !Sub ${ServiceName}-PSGEMaintWindow 102 | Schedule: rate(7 days) 103 | # Schedule: rate(5 minutes) 104 | # ScheduleOffset: Integer 105 | ScheduleTimezone: 'US/Central' 106 | StartDate: '2023-04-20T21:30:00-06:00' 107 | Tags: 108 | - Key: ServiceName 109 | Value: !Ref ServiceName 110 | - Key: StackName 111 | Value: !Ref AWS::StackName 112 | - Key: ResourceType 113 | Value: !Ref ResourceType 114 | 115 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-maintenancewindowtarget.html 116 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtarget-targets.html 117 | PSGEMaintTarget: 118 | Type: AWS::SSM::MaintenanceWindowTarget 119 | Properties: 120 | Description: Hybrid Worker Target 121 | Name: !Sub ${ServiceName}-HybridWorkerTarget 122 | OwnerInformation: PSGE SSM XML Sourcing 123 | ResourceType: INSTANCE 124 | Targets: 125 | - Key: tag:type 126 | Values: 127 | - hybridworker 128 | WindowId: !Ref PSGEMaintWindow 129 | 130 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-maintenancewindowtask.html 131 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-taskinvocationparameters.html 132 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-maintenancewindowruncommandparameters.html 133 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-cloudwatchoutputconfig.html 134 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-notificationconfig.html 135 | # https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_SendCommand.html 136 | # https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-maintenance-permissions.html#maintenance-window-tasks-service-role 137 | # https://docs.aws.amazon.com/systems-manager/latest/userguide/monitoring-sns-notifications.html 138 | # https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-maintenance-perm-console.html 139 | PSGEMaintWindowRunCommandTask: 140 | Type: 'AWS::SSM::MaintenanceWindowTask' 141 | Properties: 142 | CutoffBehavior: CANCEL_TASK 143 | Description: TBD 144 | # LoggingInfo: 145 | # LoggingInfo 146 | MaxConcurrency: 1 147 | MaxErrors: 1 148 | Name: !Sub ${ServiceName}-XMLSourcingTask 149 | Priority: 0 150 | ServiceRoleArn: !GetAtt PSGEMaintenanceWindowTaskRole.Arn 151 | Targets: 152 | - Key: WindowTargetIds 153 | Values: 154 | - !Ref PSGEMaintTarget 155 | TaskArn: AWS-RunPowerShellScript 156 | TaskInvocationParameters: 157 | MaintenanceWindowRunCommandParameters: 158 | CloudWatchOutputConfig: 159 | CloudWatchLogGroupName: !Ref PSGESSMRunCommandLogGroup 160 | CloudWatchOutputEnabled: true 161 | Comment: This is a comment 162 | # DocumentHash: String 163 | # DocumentHashType: String 164 | # DocumentVersion: String 165 | NotificationConfig: 166 | NotificationArn: !ImportValue AlertSNSTopic 167 | NotificationEvents: 168 | - TimedOut 169 | - Cancelled 170 | - Failed 171 | NotificationType: Command 172 | # OutputS3BucketName: !Ref PSGESSMLogBucket 173 | # OutputS3KeyPrefix: PSGE 174 | Parameters: 175 | executionTimeout: 176 | - '3600' 177 | commands: 178 | - !Sub if(-not(Test-Path 'C:\${ServiceName}\FinalZip\${ServiceName}.zip')){throw 'Cannot find zip file'} 179 | - !Sub $zipFilePath = 'C:\${ServiceName}\FinalZip\${ServiceName}.zip';$maxAge = New-TimeSpan -Days 4;$zipAge = New-TimeSpan -Start (Get-Item $zipFilePath).LastWriteTime -End (Get-Date);if ($zipAge -gt $maxAge) {throw 'stale zip file'} 180 | - !Sub 181 | - 'aws s3 cp "C:\${ServiceName}\FinalZip\${ServiceName}.zip" "s3://${bucketName}"' 182 | - bucketName: 183 | 'Fn::ImportValue': PubXMLDataBN 184 | - !Sub 185 | - 'aws s3 cp "C:\${ServiceName}\FinalZip\${ServiceName}.json" "s3://${bucketName}"' 186 | - bucketName: 187 | 'Fn::ImportValue': PubXMLDataBN 188 | workingDirectory: 189 | - "" 190 | ServiceRoleArn: !GetAtt PSGESSMMaintenanceWindowTaskCommandRole.Arn 191 | TimeoutSeconds: 300 192 | # TaskParameters: Json 193 | TaskType: RUN_COMMAND 194 | WindowId: !Ref PSGEMaintWindow 195 | 196 | # #https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html 197 | PSGESSMRunCommandLogGroup: 198 | Type: AWS::Logs::LogGroup 199 | DeletionPolicy: Delete 200 | UpdateReplacePolicy: Retain 201 | Properties: 202 | # DataProtectionPolicy: Json 203 | # KmsKeyId: String 204 | LogGroupName: '/aws/ssm/PSGESSMRunCommandLogGroup' 205 | RetentionInDays: 30 206 | Tags: 207 | - Key: ServiceName 208 | Value: !Ref ServiceName 209 | - Key: StackName 210 | Value: !Ref AWS::StackName 211 | - Key: ResourceType 212 | Value: !Ref ResourceType 213 | 214 | Outputs: 215 | PSGEMaintWindowID: 216 | Description: ID of PSGEMaintWindow 217 | Value: !Ref PSGEMaintWindow 218 | Export: 219 | Name: !Sub ${ServiceName}-PSGEMaintWindowID 220 | 221 | PSGEMaintTargetID: 222 | Description: ID of PSGEMaintTarget 223 | Value: !Ref PSGEMaintTarget 224 | 225 | PSGEMaintWindowRunCommandTaskID: 226 | Description: ID of PSGEMaintWindowRunCommandTask 227 | Value: !Ref PSGEMaintWindowRunCommandTask 228 | -------------------------------------------------------------------------------- /CloudFormation/PSGalleryExplorer/ControlPlane-Parameters/PSGalleryExplorer.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "ServiceName": "PSGalleryExplorer", 4 | "LMFunctionS3KeyPubXMLMonitor": "< This value is replace by the UnitTestAndBuild CodeBuild step in the Lambda's build file >", 5 | "LMFunctionHandlerPubXMLMonitor": "< This value is replace by the UnitTestAndBuild CodeBuild step in the Lambda's build file >", 6 | "ArtifactS3Bucket": "will be replaced during CodeBuild process", 7 | "ArtifactS3KeyPrefix": "will be replaced during CodeBuild process" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CloudFormation/PSGalleryExplorer/PSGalleryExplorer-ControlPlane.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | 4 | Description: 'AUTOMATED: PSGalleryExplorer: Control Plane' 5 | 6 | Parameters: 7 | ServiceName: 8 | Type: String 9 | Description: 'Name of the service that will be used to tag each resource.' 10 | 11 | LMFunctionS3KeyPubXMLMonitor: 12 | Type: String 13 | Description: S3 Key for the PubXMLMonitor Lambda function(s) zip file 14 | 15 | LMFunctionHandlerPubXMLMonitor: 16 | Type: String 17 | Description: PubXMLMonitor Lambda HANDLER provided by New-AWSPowerShellLambdaPackage during build 18 | 19 | ArtifactS3Bucket: 20 | Type: String 21 | Description: S3 Bucket for CodePipeline Artifacts 22 | 23 | ArtifactS3KeyPrefix: 24 | Type: String 25 | Description: S3 Key Prefix for CodePipeline Artifacts 26 | 27 | Resources: 28 | # Deploys the s3 buckets used for the PSGalleryExplorer project 29 | PSGES3Buckets: 30 | Type: AWS::CloudFormation::Stack 31 | DeletionPolicy: Delete 32 | UpdateReplacePolicy: Delete 33 | Properties: 34 | TemplateURL: !Sub 35 | - 'https://${ArtifactS3Bucket}.s3.amazonaws.com/${ArtifactS3KeyPrefix}/${FileName}' 36 | - #AWSRegion: !Ref "AWS::Region" 37 | ArtifactS3Bucket: !Ref ArtifactS3Bucket 38 | ArtifactS3KeyPrefix: !Ref ArtifactS3KeyPrefix 39 | FileName: CloudFormation/PSGalleryExplorer/ChildTemplates/PSGES3Buckets.yml 40 | TimeoutInMinutes: 5 #make sure it can actually complete in this time period 41 | Parameters: #much match the parameters of nested template 42 | ServiceName: !Ref ServiceName 43 | ResourceType: prod 44 | Tags: 45 | - Key: ServiceName 46 | Value: !Ref ServiceName 47 | - Key: StackName 48 | Value: !Ref AWS::StackName 49 | 50 | # Deploys the alarms used for the PSGalleryExplorer project 51 | PSGEAlarms: 52 | Type: AWS::CloudFormation::Stack 53 | DeletionPolicy: Delete 54 | UpdateReplacePolicy: Delete 55 | DependsOn: PSGES3Buckets 56 | Properties: 57 | TemplateURL: !Sub 58 | - 'https://${ArtifactS3Bucket}.s3.amazonaws.com/${ArtifactS3KeyPrefix}/${FileName}' 59 | - #AWSRegion: !Ref "AWS::Region" 60 | ArtifactS3Bucket: !Ref ArtifactS3Bucket 61 | ArtifactS3KeyPrefix: !Ref ArtifactS3KeyPrefix 62 | FileName: CloudFormation/PSGalleryExplorer/ChildTemplates/PSGEAlarms.yml 63 | TimeoutInMinutes: 5 #make sure it can actually complete in this time period 64 | Parameters: #much match the parameters of nested template 65 | ServiceName: !Ref ServiceName 66 | LambdaS3BucketName: !Ref ArtifactS3Bucket 67 | LMFunctionS3KeyPubXMLMonitor: !Ref LMFunctionS3KeyPubXMLMonitor 68 | LMFunctionHandlerPubXMLMonitor: !Ref LMFunctionHandlerPubXMLMonitor 69 | ResourceType: prod 70 | Tags: 71 | - Key: ServiceName 72 | Value: !Ref ServiceName 73 | - Key: StackName 74 | Value: !Ref AWS::StackName 75 | 76 | # Deploys the alarms used for the PSGalleryExplorer project 77 | PSGESSM: 78 | Type: AWS::CloudFormation::Stack 79 | DeletionPolicy: Delete 80 | UpdateReplacePolicy: Delete 81 | Properties: 82 | TemplateURL: !Sub 83 | - 'https://${ArtifactS3Bucket}.s3.amazonaws.com/${ArtifactS3KeyPrefix}/${FileName}' 84 | - #AWSRegion: !Ref "AWS::Region" 85 | ArtifactS3Bucket: !Ref ArtifactS3Bucket 86 | ArtifactS3KeyPrefix: !Ref ArtifactS3KeyPrefix 87 | FileName: CloudFormation/PSGalleryExplorer/ChildTemplates/PSGESSM.yml 88 | TimeoutInMinutes: 15 #make sure it can actually complete in this time period 89 | Parameters: #much match the parameters of nested template 90 | ServiceName: !Ref ServiceName 91 | ResourceType: prod 92 | Tags: 93 | - Key: ServiceName 94 | Value: !Ref ServiceName 95 | - Key: StackName 96 | Value: !Ref AWS::StackName 97 | -------------------------------------------------------------------------------- /CodeBuild/IntegrationTest/New-IntegrationInfrastructure.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This stands up any required infrastructure before the Pester integration tests run. 3 | It is invoked in the "pre_build" phase of CodeBuild. 4 | #> 5 | 6 | # $env:AWSAccountId = the AWS Account hosting the MOF Maker stack we're testing 7 | # $env:CODEBUILD_RESOLVED_SOURCE_VERSION = the CodeCommit Commit Id 8 | 9 | #$ErrorActionPreference = 'Stop' 10 | #Import-Module -Name 'AWSPowerShell.NetCore' 11 | -------------------------------------------------------------------------------- /CodeBuild/IntegrationTest/Remove-IntegrationInfrastructure.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This removes any required infrastructure after the Pester integration tests run. 3 | It is invoked whether the build succeeds or fails, and is invoked in the 4 | "post_build" phase of CodeBuild. 5 | #> 6 | 7 | # $env:AWSAccountId = the AWS Account hosting the MOF Maker stack we're testing 8 | # $env:CODEBUILD_RESOLVED_SOURCE_VERSION = the CodeCommit Commit Id 9 | 10 | #$ErrorActionPreference = 'Stop' 11 | #Import-Module -Name 'AWSPowerShell.NetCore' 12 | -------------------------------------------------------------------------------- /CodeBuild/IntegrationTest/Tests/Integration.Tests.ps1: -------------------------------------------------------------------------------- 1 | # $env:ARTIFACT_S3_BUCKET = the artifact bucket used by CB 2 | # $env:AWSAccountId = the AWS Account hosting the service under test 3 | # $env:AWSRegion = the AWS Region hosting the service under test 4 | # $env:projectName = name of the project 5 | 6 | Describe -Name 'Infrastructure Tests' -Fixture { 7 | BeforeAll { 8 | try { 9 | $cfnExports = Get-CFNExport -ErrorAction Stop 10 | } 11 | catch { 12 | throw 13 | } 14 | $script:ServiceName = $env:projectName 15 | } #before_all 16 | 17 | Context -Name 'PSGES3Buckets.yml' { 18 | 19 | It 'Created the S3 buckets needed for the project' -Test { 20 | $assertion1 = ($cfnExports | Where-Object { $_.Name -eq 'StageTriggerBN' }).Value 21 | $expected = 'psge-*' 22 | $assertion1 | Should -BeLike $expected 23 | $assertion2 = ($cfnExports | Where-Object { $_.Name -eq 'GitXMLDataBN' }).Value 24 | $expected = 'psge-*' 25 | $assertion2 | Should -BeLike $expected 26 | $assertion4 = ($cfnExports | Where-Object { $_.Name -eq 'PubXMLDataBN' }).Value 27 | $expected = 'psge-*' 28 | $assertion4 | Should -BeLike $expected 29 | $logEval = ($cfnExports | Where-Object { $_.Name -eq 'PSGECloudFrontLogBucketBN' }).Value 30 | $logEval | Should -Not -BeNullOrEmpty 31 | } #it 32 | 33 | It 'Should create a SNS topic for S3 trigger updates' -Test { 34 | $assertion = ($cfnExports | Where-Object { $_.Name -eq 'UpdateSNSTopicArn' }).Value 35 | $expected = 'arn:aws:sns:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 36 | $assertion | Should -BeLike $expected 37 | } #it 38 | 39 | It -Name 'Should create a CloudFront distribution' -Test { 40 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-PSGECloudFrontDistributionDomain" }).Value 41 | $expected = '*.cloudfront.net' 42 | $assertion | Should -BeLike $expected 43 | } #it 44 | 45 | } #context_PSGES3Buckets 46 | 47 | Context -Name 'PSGE.yml' { 48 | 49 | It -Name 'Should create a GitHubDataDlqArn' -Test { 50 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitHubDataDlqARN" }).Value 51 | $expected = 'arn:aws:sqs:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 52 | $assertion | Should -BeLike $expected 53 | } #it 54 | 55 | It -Name 'Should create a GitHubDataQueueArn' -Test { 56 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitHubDataQueueARN" }).Value 57 | $expected = 'arn:aws:sqs:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 58 | $assertion | Should -BeLike $expected 59 | } #it 60 | 61 | It -Name 'Should create a GitLabDataDlqARN' -Test { 62 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitLabDataDlqARN" }).Value 63 | $expected = 'arn:aws:sqs:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 64 | $assertion | Should -BeLike $expected 65 | } #it 66 | 67 | It -Name 'Should create a GitLabDataQueueARN' -Test { 68 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitLabDataQueueARN" }).Value 69 | $expected = 'arn:aws:sqs:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 70 | $assertion | Should -BeLike $expected 71 | } #it 72 | 73 | It -Name 'Should create a UpdatePubXMLDataQueueARN' -Test { 74 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-UpdatePubXMLDataQueueARN" }).Value 75 | $expected = 'arn:aws:sqs:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 76 | $assertion | Should -BeLike $expected 77 | } #it 78 | 79 | It -Name 'Should create a GitHubScannerSMDelayStateMachineARN' -Test { 80 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitHubScannerSMDelayStateMachineARN" }).Value 81 | $expected = 'arn:aws:states:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 82 | $assertion | Should -BeLike $expected 83 | } #it 84 | 85 | It -Name 'Should create a GitLabScannerSMDelayStateMachineArn' -Test { 86 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitLabScannerSMDelayStateMachineArn" }).Value 87 | $expected = 'arn:aws:states:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 88 | $assertion | Should -BeLike $expected 89 | } #it 90 | 91 | It -Name 'Should create a GSScheduledRuleARN' -Test { 92 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GSScheduledRuleARN" }).Value 93 | $expected = 'arn:aws:events:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 94 | $assertion | Should -BeLike $expected 95 | } #it 96 | 97 | It -Name 'Should create a PubXMLPopulatorARN' -Test { 98 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-PubXMLPopulatorARN" }).Value 99 | $expected = 'arn:aws:lambda:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 100 | $assertion | Should -BeLike $expected 101 | } #it 102 | 103 | It -Name 'Should create a GalleryScannerARN' -Test { 104 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GalleryScannerARN" }).Value 105 | $expected = 'arn:aws:lambda:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 106 | $assertion | Should -BeLike $expected 107 | } #it 108 | 109 | It -Name 'Should create a GitHubScannerARN' -Test { 110 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitHubScannerARN" }).Value 111 | $expected = 'arn:aws:lambda:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 112 | $assertion | Should -BeLike $expected 113 | } #it 114 | 115 | It -Name 'Should create a GitHubSMScannerARN' -Test { 116 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitHubSMScannerARN" }).Value 117 | $expected = 'arn:aws:lambda:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 118 | $assertion | Should -BeLike $expected 119 | } #it 120 | 121 | It -Name 'Should create a GitLabScannerARN' -Test { 122 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitLabScannerARN" }).Value 123 | $expected = 'arn:aws:lambda:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 124 | $assertion | Should -BeLike $expected 125 | } #it 126 | 127 | It -Name 'Should create a GitLabSMScannerARN' -Test { 128 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitLabSMScannerARN" }).Value 129 | $expected = 'arn:aws:lambda:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 130 | $assertion | Should -BeLike $expected 131 | } #it 132 | 133 | It -Name 'Should create a GCombineArn' -Test { 134 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GCombineArn" }).Value 135 | $expected = 'arn:aws:lambda:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 136 | $assertion | Should -BeLike $expected 137 | } #it 138 | 139 | } #context_PSGE 140 | 141 | Context -Name 'PSGEAlarms.yml' { 142 | 143 | It -Name 'Should create a GitHubSMFailureAlarmARN' -Test { 144 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitHubSMFailureAlarmARN" }).Value 145 | $expected = 'arn:aws:cloudwatch:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 146 | $assertion | Should -BeLike $expected 147 | } #it 148 | 149 | It -Name 'Should create a GitHubSMThrottleAlarmARN' -Test { 150 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitHubSMThrottleAlarmARN" }).Value 151 | $expected = 'arn:aws:cloudwatch:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 152 | $assertion | Should -BeLike $expected 153 | } #it 154 | 155 | It -Name 'Should create a GitHubSMTimedOutAlarmARN' -Test { 156 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitHubSMTimedOutAlarmARN" }).Value 157 | $expected = 'arn:aws:cloudwatch:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 158 | $assertion | Should -BeLike $expected 159 | } #it 160 | 161 | It -Name 'Should create a GitLabSMThrottleAlarmARN' -Test { 162 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitLabSMThrottleAlarmARN" }).Value 163 | $expected = 'arn:aws:cloudwatch:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 164 | $assertion | Should -BeLike $expected 165 | } #it 166 | 167 | It -Name 'Should create a GitLabSMTimedOutAlarmARN' -Test { 168 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-GitLabSMTimedOutAlarmARN" }).Value 169 | $expected = 'arn:aws:cloudwatch:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 170 | $assertion | Should -BeLike $expected 171 | } #it 172 | 173 | It -Name 'Should create a PubXMLMonitorAlarmARN' -Test { 174 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-PubXMLMonitorAlarmARN" }).Value 175 | $expected = 'arn:aws:cloudwatch:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 176 | $assertion | Should -BeLike $expected 177 | } #it 178 | 179 | It -Name 'Should create a PubXMLMonitorARN' -Test { 180 | $assertion = ($cfnExports | Where-Object { $_.Name -eq "$ServiceName-PubXMLMonitorARN" }).Value 181 | $expected = 'arn:aws:lambda:{0}:{1}:*' -f $env:AWSRegion, $env:AWSAccountId 182 | $assertion | Should -BeLike $expected 183 | } #it 184 | 185 | } #context_PSGEAlarms 186 | 187 | } #describe_infra_tests 188 | -------------------------------------------------------------------------------- /CodeBuild/IntegrationTest/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | runtime-versions: 6 | dotnet: 6.0 7 | commands: 8 | - echo Configure AWS defaults using the configuration script added to the Docker Image. 9 | - pwsh -command './CodeBuild/configure_aws_credential.ps1' 10 | - echo Installing PowerShell Modules from S3 11 | - pwsh -command './CodeBuild/install_modules.ps1' 12 | pre_build: 13 | commands: 14 | - pwsh -command '$PSVersionTable' 15 | - pwsh -command 'Get-ChildItem env:' 16 | - echo Pre-Build started on `date` 17 | - pwsh -command '& ./CodeBuild/IntegrationTest/New-IntegrationInfrastructure.ps1' 18 | build: 19 | commands: 20 | - pwsh -command ' 21 | Import-Module Pester; 22 | $pesterConfiguration = [PesterConfiguration]::new(); 23 | $pesterConfiguration.Run.Exit = $true; 24 | $pesterConfiguration.Run.Path = "./CodeBuild/IntegrationTest/Tests"; 25 | $pesterConfiguration.Output.Verbosity = "Detailed"; 26 | Invoke-Pester -Configuration $pesterConfiguration' 27 | finally: 28 | - pwsh -command '& ./CodeBuild/IntegrationTest/Remove-IntegrationInfrastructure.ps1' 29 | -------------------------------------------------------------------------------- /CodeBuild/UnitTestAndBuild/Publish-CFNTemplatesToS3.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | $ProgressPreference = 'SilentlyContinue' 3 | 4 | if ([String]::IsNullOrWhiteSpace($env:ARTIFACT_S3_BUCKET)) { 5 | throw 'The environment variable ARTIFACT_S3_BUCKET must be configured.' 6 | } 7 | 8 | if ([String]::IsNullOrWhiteSpace($env:S3_KEY_PREFIX)) { 9 | throw 'The environment variable S3_KEY_PREFIX must be configured.' 10 | } 11 | 12 | # Paths are based from the root of the GIT repository 13 | $paths = @( 14 | './CloudFormation' 15 | ) 16 | 17 | if ($env:REGIONAL_BUCKET) { 18 | $artifactBucket = $env:REGIONAL_BUCKET 19 | } 20 | else { 21 | $artifactBucket = $env:ARTIFACT_S3_BUCKET 22 | } 23 | 24 | foreach ($path in $paths) { 25 | Write-Host ('Processing CloudFormation Templates in {0}' -f $path) 26 | # All CloudFormation Templates to publish are located in or below the "./CloudFormation" folder 27 | foreach ($file in (Get-ChildItem -Path $path -Recurse -File -Filter "*.yml")) { 28 | 29 | '' # Blank line to separate CodeBuild Output 30 | 31 | # Calculate the S3 Key Prefix, keeping the correct Folder Structure 32 | if ($file.Name -like '*-ControlPlane.yml') { 33 | $s3KeyPrefix = '{0}/CloudFormation/{1}' -f $env:S3_KEY_PREFIX, $file.Directory.Name 34 | } 35 | elseif ($file.Directory.Name -eq 'ChildTemplates') { 36 | # Find the parent template path 37 | $parentPath = Split-Path -Path $file.Directory 38 | $templatePath = Split-Path -Path $parentPath -Leaf 39 | $s3KeyPrefix = '{0}/CloudFormation/{1}/{2}' -f $env:S3_KEY_PREFIX, $templatePath, $file.Directory.Name 40 | } 41 | elseif ($file.Directory.Name -eq 'Manual') { 42 | Write-Host 'Manually deployed CFN detected. Skipping.' 43 | continue 44 | } 45 | else { 46 | throw 'Unexpected directory encountered inside CloudFormation folder' 47 | } 48 | 49 | $s3Key = '{0}/{1}' -f $s3KeyPrefix, $file.Name 50 | [string]$endPoint = "https://s3.$env:AWSRegion" + ".amazonaws.com" 51 | 52 | Write-Host ('ENDPOINT: {0}' -f $endPoint) 53 | Write-Host ('BUCKET: {0}' -f $artifactBucket) 54 | Write-Host ('KEY: {0}' -f $s3Key) 55 | $writeS3ObjectSplat = @{ 56 | BucketName = $artifactBucket 57 | Key = $s3Key 58 | File = $file.FullName 59 | Region = $env:AWSRegion 60 | EndpointUrl = $endPoint 61 | } 62 | Write-S3Object @writeS3ObjectSplat 63 | 64 | Remove-Variable -Name @('s3Key', 's3KeyPrefix') -ErrorAction SilentlyContinue 65 | } 66 | } 67 | # Update the ControlPlane Parameters JSON files with the target Artifact S3 Bucket. 68 | # All JSON files will be updated, however the deployment CodePipeline is hard coded 69 | # to a specific JSON file so other deployments will not be affected. 70 | foreach ($controlPlaneFolder in (Get-ChildItem -Path './CloudFormation' -Recurse -Filter '*ControlPlane-Parameters' -Directory)) { 71 | foreach ($file in (Get-ChildItem -Path $controlPlaneFolder.FullName -Filter '*.json' -Recurse)) { 72 | $fileContent = Get-Content -Path $file.FullName -Raw | ConvertFrom-Json 73 | 74 | $fileContent.Parameters.ArtifactS3Bucket = $artifactBucket 75 | 76 | $fileContent.Parameters.ArtifactS3KeyPrefix = $env:S3_KEY_PREFIX 77 | 78 | $fileContent | ConvertTo-Json -Compress -Depth 6 | Out-File -FilePath $file.FullName -Force 79 | } 80 | } -------------------------------------------------------------------------------- /CodeBuild/UnitTestAndBuild/Validate-CFNTemplates.Tests.ps1: -------------------------------------------------------------------------------- 1 | $paths = @( 2 | 'CloudFormation' 3 | ) 4 | $files = Get-ChildItem -Path $paths -Recurse -Filter '*.yml' 5 | $ErrorActionPreference = 'Stop' 6 | 7 | Describe 'CloudFormation Template Validation' { 8 | 9 | Context -Name 'CloudFormation Templates' { 10 | Context -Name '<_.Name>' -Foreach $files { 11 | It 'is valid CFN' { 12 | { Test-CFNTemplate -TemplateBody (Get-Content -Path $_.FullName -Raw) } | Should -Not -Throw 13 | } #it 14 | } 15 | } #context_cfn_templates 16 | 17 | } #describe_cfn_templates 18 | -------------------------------------------------------------------------------- /CodeBuild/UnitTestAndBuild/Validate-JSONConfigurations.Tests.ps1: -------------------------------------------------------------------------------- 1 | $paths = @( 2 | 'CloudFormation' 3 | ) 4 | $files = Get-ChildItem -Path $paths -File -Recurse -Filter '*.json' 5 | $ErrorActionPreference = 'Stop' 6 | 7 | Describe -Name 'JSON Configuration File Validation' { 8 | 9 | Context -Name 'JSON Parameter Files' { 10 | Context -Name '<_.Name>' -Foreach $files { 11 | It 'is valid JSON' { 12 | { $null = ConvertFrom-Json -InputObject (Get-Content -Path $_.FullName -Raw) } | Should -Not -Throw 13 | } #it 14 | } 15 | } #context_json_parameter_files 16 | 17 | } #describe_json_configuration_file_validation 18 | -------------------------------------------------------------------------------- /CodeBuild/UnitTestAndBuild/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | runtime-versions: 6 | dotnet: 6.0 7 | # python: 3.8 8 | commands: 9 | # Check PowerShell Version 10 | - pwsh -command '$PSVersionTable' 11 | 12 | # Check available variables in the build 13 | # - pwsh -command 'Get-Variable' 14 | 15 | # Check available environment variables in the build 16 | # - pwsh -command 'Get-ChildItem env:' 17 | 18 | # Configure AWS defaults using the configuration script added to the Docker Image. 19 | - pwsh -command './CodeBuild/configure_aws_credential.ps1' 20 | 21 | # Installing PowerShell Modules from S3/Gallery 22 | - pwsh -command './CodeBuild/install_modules.ps1' 23 | 24 | # Install Python packages 25 | # - pip install pip -U 26 | # - pip install -r ./lambdafunctions/python/requirements.txt 27 | 28 | pre_build: 29 | commands: 30 | # https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script 31 | # - pwsh -command '& ./CodeBuild/UnitTestAndBuild/dotnet-install.ps1 -Channel LTS' 32 | # - ./CodeBuild/UnitTestAndBuild/dotnet-install.sh --channel LTS 33 | 34 | # Validate CloudFormation Templates 35 | - pwsh -command ' 36 | Import-Module Pester; 37 | $pesterConfiguration = [PesterConfiguration]::new(); 38 | $pesterConfiguration.Run.Exit = $true; 39 | $pesterConfiguration.Run.Path = "./CodeBuild/UnitTestAndBuild/Validate-CFNTemplates.Tests.ps1"; 40 | $pesterConfiguration.Output.Verbosity = "Detailed"; 41 | Invoke-Pester -Configuration $pesterConfiguration' 42 | 43 | # Validate Json files 44 | - pwsh -command ' 45 | Import-Module Pester; 46 | $pesterConfiguration = [PesterConfiguration]::new(); 47 | $pesterConfiguration.Run.Exit = $true; 48 | $pesterConfiguration.Run.Path = "./CodeBuild/UnitTestAndBuild/Validate-JSONConfigurations.Tests.ps1"; 49 | $pesterConfiguration.Output.Verbosity = "Detailed"; 50 | Invoke-Pester -Configuration $pesterConfiguration' 51 | 52 | build: 53 | commands: 54 | 55 | # Publish CloudFormation templates to S3 56 | - pwsh -command 'Import-Module -Name "AWS.Tools.Common";Import-Module -Name "AWS.Tools.S3"; & ./CodeBuild/UnitTestAndBuild/Publish-CFNTemplatesToS3.ps1' 57 | 58 | # Running Invoke-Build against each PowerShell AWS Lambda Function 59 | - pwsh -command 'Get-ChildItem -Path './lambdafunctions/PowerShell' -Filter '*.build.ps1' -File -Recurse | ForEach-Object {Invoke-Build -File $_.FullName}' 60 | 61 | artifacts: 62 | files: 63 | - CloudFormation/**/* 64 | - CodeBuild/**/* 65 | -------------------------------------------------------------------------------- /CodeBuild/configure_aws_credential.ps1: -------------------------------------------------------------------------------- 1 | 'Configuring AWS credentials' 2 | 3 | ' - Retrieving temporary credentials from metadata' 4 | $uri = 'http://169.254.170.2{0}' -f $env:AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 5 | $sts = Invoke-RestMethod -UseBasicParsing -Uri $uri 6 | 7 | ' - Setting default AWS Credential' 8 | $credentialsFile = '~/.aws/credentials' 9 | $null = New-Item -Path $credentialsFile -Force 10 | 11 | '[default]' | Out-File -FilePath $credentialsFile -Append 12 | 'aws_access_key_id={0}' -f $sts.AccessKeyId | Out-File -FilePath $credentialsFile -Append 13 | 'aws_secret_access_key={0}' -f $sts.SecretAccessKey | Out-File -FilePath $credentialsFile -Append 14 | 'aws_session_token={0}' -f $sts.Token | Out-File -FilePath $credentialsFile -Append 15 | 16 | ' - Setting default AWS Region' 17 | $null = New-Item -Path $profile -Force 18 | '$StoredAWSRegion = "{0}"' -f $env:AWS_DEFAULT_REGION | Out-File -FilePath $profile -Force 19 | 20 | ' - AWS credentials configured' 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jake Morrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSGalleryExplorer 2 | 3 | [![Minimum Supported PowerShell Version](https://img.shields.io/badge/PowerShell-5.1+-blue.svg)](https://github.com/PowerShell/PowerShell) [![PowerShell Gallery][psgallery-img]][psgallery-site] ![Cross Platform](https://img.shields.io/badge/platform-windows%20%7C%20macos%20%7C%20linux-lightgrey) [![License][license-badge]](LICENSE) [![Documentation Status](https://readthedocs.org/projects/psgalleryexplorer/badge/?version=latest)](https://psgalleryexplorer.readthedocs.io/en/latest/?badge=latest) 4 | 5 | [psgallery-img]: https://img.shields.io/powershellgallery/dt/PSGalleryExplorer?label=Powershell%20Gallery&logo=powershell 6 | [psgallery-site]: https://www.powershellgallery.com/packages/PSGalleryExplorer 7 | [psgallery-v1]: https://www.powershellgallery.com/packages/PSGalleryExplorer/0.8.0 8 | [license-badge]: https://img.shields.io/github/license/techthoughts2/PSGalleryExplorer 9 | 10 |

11 | PSGalleryExplorer Logo 12 |

13 | 14 | Branch | Windows | Windows pwsh | MacOS | Linux 15 | --- | --- | --- | --- | --- | 16 | main | ![M-W-Build Status](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiYURpTUhFRkQ0aXZMMWRnTEl3U2x3Q0VaYWtNWHFEVTBuOTNhaXZLV1ZNczNWc0tHUEJkdzhDajR0Q2pERXl0c3huM01DdVliU3YzU3NwT0hnNHM5Rm5VPSIsIml2UGFyYW1ldGVyU3BlYyI6Ikk4Z1hDdENXZ0RZSDdoaXUiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=main) | ![M-W-pwsh-Build Status](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoicGN3NzBiY1dPRlRPSXpsQmF5dEJ6aTVVWWx4a3h6M2R6dTA1dmF2UmxEWUppOWpwR0hMU3lINUdxMElCUlJIc0RWRTZFSzRlNXM2S2RMY3UzdlZaRnFRPSIsIml2UGFyYW1ldGVyU3BlYyI6InZUbVhaMjBlc09KQWVlckMiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=main) | [![M-Mac-Build status](https://ci.appveyor.com/api/projects/status/s28ivs5pavul6usq/branch/main?svg=true)](https://ci.appveyor.com/project/techthoughts2/psgalleryexplorer/branch/main) | ![M-L-Build Status](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiRDBjRkVOTVhHTmJybmRFZ216QlM1ekNEMjBMR3paN3VNMUlHMW9QNXIwaUxIQU5oUm9pbVZtZndWSEl3Mzh6YVQ3NitCREk2YnRoVjJMYUtBcno4WlRVPSIsIml2UGFyYW1ldGVyU3BlYyI6IjRaWFJ6dXMzOFdFaWVWM0giLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=main) | 17 | Enhancements | ![E-W-Build Status](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoidFV4dnZKQmd4cHZMSE0zdFRmQml0NDMvZzh4cGJzMVEwbGFTQVBaMmpya2tRcTJXbXZmV00xeGF0WUpST0lJczFoRTROUitlZXAvWGNpN3ErV0s1VWVnPSIsIml2UGFyYW1ldGVyU3BlYyI6IlA2aUQyYm9OSWswS2Q5ZEciLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=Enhancements) | ![E-W-pwsh-Build Status](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiWlAyKzRLYVFybDdLdjJFcHdkeHNpcTdxZnRFMytJeVVYRWRCTUN6SFZVRENrZW51dHlnOFVydS9CMkp0YnhiQlY4WDV0YmlmdFBUUy96S1ZLT1BVdnVrPSIsIml2UGFyYW1ldGVyU3BlYyI6InRaUDIvS3Vnb2dMbkEyQVciLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=Enhancements) | [![E-Mac-Build status](https://ci.appveyor.com/api/projects/status/s28ivs5pavul6usq/branch/Enhancements?svg=true)](https://ci.appveyor.com/project/techthoughts2/psgalleryexplorer/branch/Enhancements)| ![E-L-Build Status](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiQmRrOU9HOUppVHAxRVNhZlpTT3BNU2phSzRSSVQ3aysyemRDSjJiU3NDZEU1QzRDNlBMM0Jnai9qb2RNektUWktudUp3OWN6UTJQZEFnZlNZRG1GZ3ZZPSIsIml2UGFyYW1ldGVyU3BlYyI6IlVudjNoc3JaMnZQU2ljV1UiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=Enhancements) | 18 | 19 | ## Synopsis 20 | 21 | PSGalleryExplorer is a PowerShell module that extends the search functionality of the PowerShell Gallery by providing additional project information about modules. This enables you to search, explore, and discover PowerShell Gallery modules based on additional criteria. 22 | 23 | ![PSGalleryExplorer Gif Demo](docs/assets/psgalleryexplorer.gif) 24 | 25 | ## Description 26 | 27 | PSGalleryExplorer is a PowerShell module that extends the search functionality of the PowerShell Gallery by providing additional project information about modules. It enables users to search, explore, and discover PowerShell Gallery modules based on additional criteria that are not available via `Find-Module`. The module provides various features such as filtering results based on download counts, stars, forks, and repository health metrics like open issues, license, and last updated date. With PSGalleryExplorer, users can easily identify trending and actively developed modules, and explore module repositories directly from the console. 28 | 29 | With `Find-ModuleByCommand`, PSGalleryExplorer now provides even more value to users by enabling them to quickly locate modules that contain specific commands. This enables you to search for modules by function name, even if you do not have the module locally installed. 30 | 31 | ### Features 32 | 33 | - Fully cross-platform and can be run on Windows, Linux, and macOS 34 | - Discover modules based on various criteria such as number of downloads, stars, forks, and more 35 | - Get insights into the community health of a module's repository, including information about open issues, license, and last updated date 36 | - Identify modules that are actively being developed by filtering based on their most recent repository update date. 37 | - Compliments existing tools like `Find-Module` to provide another way to explore modules on the PowerShell Gallery. 38 | - Identify up-and-coming or trending modules by comparing search results including and excluding popular and corporate modules 39 | - PSGalleryExplorer provides a detailed, informative output of module results to help you quickly identify prime candidates for further exploration. 40 | - `Find-ModuleByCommand` allows users to search for modules based on a specific command name, even if the module is not installed locally, providing a quick and easy way to locate modules containing the desired functionality. 41 | 42 | ## Getting Started 43 | 44 | ### Documentation 45 | 46 | Documentation for PSGalleryExplorer is available at: [https://psgalleryexplorer.readthedocs.io](https://psgalleryexplorer.readthedocs.io) 47 | 48 | ### Installation 49 | 50 | ```powershell 51 | # Install PSGalleryExplorer from the PowerShell Gallery 52 | Install-Module -Name "PSGalleryExplorer" -Repository PSGallery -Scope CurrentUser 53 | ``` 54 | 55 | ### Quick start 56 | 57 | ```powershell 58 | #------------------------------------------------------------------------------------------------ 59 | # import the PSGalleryExplorer module 60 | Import-Module -Name "PSGalleryExplorer" 61 | #------------------------------------------------------------------------------------------------ 62 | # discover module info by tag 63 | Find-PSGModule -ByTag Telegram 64 | #------------------------------------------------------------------------------------------------ 65 | # discover PowerShell modules by # of Gallery Downloads 66 | Find-PSGModule -ByDownloads 67 | #------------------------------------------------------------------------------------------------ 68 | # discover the most recently updated modules on repo 69 | Find-PSGModule -ByRecentUpdate RepoUpdate 70 | #------------------------------------------------------------------------------------------------ 71 | # discover the most recently updated modules on the PowerShell Gallery 72 | Find-PSGModule -ByRecentUpdate GalleryUpdate 73 | #------------------------------------------------------------------------------------------------ 74 | # discover PowerShell modules by # of Gallery Downloads 75 | # include corporate modules and common/popular modules in results 76 | # return top 50 77 | Find-PSGModule -ByDownloads -IncludeCorps -IncludeRegulars -NumberToReturn 50 78 | #------------------------------------------------------------------------------------------------ 79 | # discover PowerShell modules by # of repo project stars 80 | Find-PSGModule -ByRepoInfo StarCount 81 | #------------------------------------------------------------------------------------------------ 82 | # discover PowerShell modules that could possibly use some help 83 | Find-PSGModule -ByRepoInfo Issues 84 | #------------------------------------------------------------------------------------------------ 85 | # discover PowerShell modules by # of repo project subscribers 86 | Find-PSGModule -ByRepoInfo Subscribers 87 | #------------------------------------------------------------------------------------------------ 88 | # discover a set of random modules 89 | Find-PSGModule -ByRandom 90 | #------------------------------------------------------------------------------------------------ 91 | # discover module info by name 92 | Find-PSGModule -ByName 'PoshGram' 93 | #------------------------------------------------------------------------------------------------ 94 | # Returns a list of modules that contain the command Send-TelegramTextMessage 95 | Find-ModuleByCommand -CommandName 'Send-TelegramTextMessage' 96 | #------------------------------------------------------------------------------------------------ 97 | ``` 98 | 99 | ## Notes 100 | 101 | This PowerShell project was created with [Catesta](https://github.com/techthoughts2/Catesta). 102 | 103 | ## Contributing 104 | 105 | If you'd like to contribute to PSGalleryExplorer, please see the [contribution guidelines](.github/CONTRIBUTING.md). 106 | 107 | ## License 108 | 109 | This project is [licensed under the MIT License](LICENSE). 110 | -------------------------------------------------------------------------------- /actions_bootstrap.ps1: -------------------------------------------------------------------------------- 1 | # Bootstrap dependencies 2 | 3 | # https://docs.microsoft.com/powershell/module/packagemanagement/get-packageprovider 4 | Get-PackageProvider -Name Nuget -ForceBootstrap | Out-Null 5 | 6 | # https://docs.microsoft.com/powershell/module/powershellget/set-psrepository 7 | Set-PSRepository -Name PSGallery -InstallationPolicy Trusted 8 | 9 | # List of PowerShell Modules required for the build 10 | $modulesToInstall = [System.Collections.ArrayList]::new() 11 | # https://github.com/pester/Pester 12 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 13 | ModuleName = 'Pester' 14 | ModuleVersion = '5.4.0' 15 | })) 16 | # https://github.com/nightroman/Invoke-Build 17 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 18 | ModuleName = 'InvokeBuild' 19 | ModuleVersion = '5.10.3' 20 | })) 21 | # https://github.com/PowerShell/PSScriptAnalyzer 22 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 23 | ModuleName = 'PSScriptAnalyzer' 24 | ModuleVersion = '1.21.0' 25 | })) 26 | # https://github.com/PowerShell/platyPS 27 | # older version used due to: https://github.com/PowerShell/platyPS/issues/457 28 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 29 | ModuleName = 'platyPS' 30 | ModuleVersion = '0.12.0' 31 | })) 32 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 33 | ModuleName = 'Convert' 34 | ModuleVersion = '1.5.0' 35 | })) 36 | 37 | 'Installing PowerShell Modules' 38 | foreach ($module in $modulesToInstall) { 39 | $installSplat = @{ 40 | Name = $module.ModuleName 41 | RequiredVersion = $module.ModuleVersion 42 | Repository = 'PSGallery' 43 | Force = $true 44 | ErrorAction = 'Stop' 45 | } 46 | try { 47 | Install-Module @installSplat 48 | Import-Module -Name $module.ModuleName -ErrorAction Stop 49 | ' - Successfully installed {0}' -f $module.ModuleName 50 | } 51 | catch { 52 | $message = 'Failed to install {0}' -f $module.ModuleName 53 | " - $message" 54 | throw $message 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | 3 | # https://www.appveyor.com/docs/build-configuration/ 4 | # https://www.appveyor.com/docs/build-configuration/#specializing-matrix-job-configuration 5 | # https://www.appveyor.com/docs/appveyor-yml/ 6 | 7 | branches: 8 | only: 9 | - main 10 | - Enhancements 11 | 12 | # Do not build on tags (GitHub, Bitbucket, GitLab, Gitea) 13 | skip_tags: true 14 | 15 | skip_commits: 16 | files: 17 | - docs/* 18 | - media/* 19 | message: /updated readme.*|update readme.*s/ 20 | 21 | image: 22 | - macOS 23 | 24 | init: 25 | - pwsh: $PSVersionTable 26 | # uncomment the line below to explore what modules/variables/env variables are available in the build image 27 | # - pwsh: Get-Module -ListAvailable; (Get-Variable).GetEnumerator() | Sort-Object Name | Out-String; (Get-ChildItem env:*).GetEnumerator() | Sort-Object Name | Out-String 28 | install: 29 | - pwsh: . .\actions_bootstrap.ps1 30 | build_script: 31 | - pwsh: Invoke-Build -File .\src\PSGalleryExplorer.build.ps1 32 | 33 | artifacts: 34 | - path: src/Artifacts/testOutput/PesterTests.xml 35 | name: PesterTestResults 36 | - path: src/Artifacts/ccReport/CodeCoverage.xml 37 | name: CodeCoverageResults 38 | - path: src/Archive/*.zip 39 | name: BuildArtifact 40 | -------------------------------------------------------------------------------- /buildspec_powershell_windows.yml: -------------------------------------------------------------------------------- 1 | # This is a simple CodeBuild build file for PowerShell. 2 | # - pre_build step will ensure the Module Name / Version has not previously been built for production (plans to add this at a later time) 3 | # - build step will perform Clean, ValidateRequirements, Analyze, Test, CreateHelp, Build, Archive 4 | # https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html 5 | # https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#runtime-versions-buildspec-file 6 | # https://docs.aws.amazon.com/codebuild/latest/userguide/test-reporting.html 7 | 8 | version: 0.2 9 | 10 | phases: 11 | install: 12 | commands: 13 | - powershell -command 'Install-PackageProvider -Name "NuGet" -Confirm:$false -Force -Verbose' 14 | - powershell -command 'Install-Module -Name PowerShellGet -Repository PSGallery -Force' 15 | - powershell -command '.\configure_aws_credential.ps1' 16 | - powershell -command '.\install_modules.ps1' 17 | pre_build: 18 | commands: 19 | # - powershell -command 'New-Item -Path C:\Test -ItemType Directory | Out-Null' 20 | - powershell -command '$PSVersionTable' 21 | # uncomment the line below to explore what modules/variables/env variables are available in the build image 22 | # - powershell -command 'Get-Module -ListAvailable; (Get-Variable).GetEnumerator() | Sort-Object Name | Out-String; (Get-ChildItem env:*).GetEnumerator() | Sort-Object Name | Out-String' 23 | build: 24 | commands: 25 | - powershell -command 'Invoke-Build -File .\src\PSGalleryExplorer.build.ps1' 26 | artifacts: 27 | files: 28 | - '**/*' 29 | base-directory: 'src\Archive' 30 | reports: 31 | PesterTestReport: 32 | files: 33 | - '**/*' 34 | file-format: NunitXml 35 | base-directory: 'src\Artifacts\testOutput' 36 | CodeCoverageReport: 37 | files: 38 | - 'src\Artifacts\ccReport\CodeCoverage.xml' 39 | file-format: 'JaCoCoXml' -------------------------------------------------------------------------------- /buildspec_pwsh_linux.yml: -------------------------------------------------------------------------------- 1 | # This is a simple CodeBuild build file for pwsh. 2 | # - pre_build step will ensure the Module Name / Version has not previously been built for production (plans to add this at a later time) 3 | # - build step will perform Clean, ValidateRequirements, Analyze, Test, CreateHelp, Build, Archive 4 | # https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html 5 | # https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#runtime-versions-buildspec-file 6 | # https://docs.aws.amazon.com/codebuild/latest/userguide/test-reporting.html 7 | 8 | version: 0.2 9 | 10 | phases: 11 | install: 12 | runtime-versions: 13 | dotnet: 6.0 14 | commands: 15 | - pwsh -command './configure_aws_credential.ps1' 16 | - pwsh -command './install_modules.ps1' 17 | pre_build: 18 | commands: 19 | # - pwsh -command 'New-Item -Path /Test -ItemType Directory | Out-Null' 20 | - pwsh -command '$PSVersionTable' 21 | # uncomment the line below to explore what modules/variables/env variables are available in the build image 22 | # - pwsh -command 'Get-Module -ListAvailable; (Get-Variable).GetEnumerator() | Sort-Object Name | Out-String; (Get-ChildItem env:*).GetEnumerator() | Sort-Object Name | Out-String' 23 | build: 24 | commands: 25 | - pwsh -command 'Invoke-Build -File .\src\PSGalleryExplorer.build.ps1' 26 | artifacts: 27 | files: 28 | - '**/*' 29 | base-directory: 'src/Archive' 30 | reports: 31 | PesterTestReport: 32 | files: 33 | - '**/*' 34 | file-format: NunitXml 35 | base-directory: 'src/Artifacts/testOutput' 36 | CodeCoverageReport: 37 | files: 38 | - 'src/Artifacts/ccReport/CodeCoverage.xml' 39 | file-format: 'JaCoCoXml' 40 | -------------------------------------------------------------------------------- /buildspec_pwsh_windows.yml: -------------------------------------------------------------------------------- 1 | # This is a simple CodeBuild build file for PowerShell. 2 | # - pre_build step will ensure the Module Name / Version has not previously been built for production (plans to add this at a later time) 3 | # - build step will perform Clean, ValidateRequirements, Analyze, Test, CreateHelp, Build, Archive 4 | # https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html 5 | # https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#runtime-versions-buildspec-file 6 | # https://docs.aws.amazon.com/codebuild/latest/userguide/test-reporting.html 7 | 8 | version: 0.2 9 | 10 | phases: 11 | install: 12 | runtime-versions: 13 | dotnet: 6.0 14 | commands: 15 | - pwsh -command '.\configure_aws_credential.ps1' 16 | - pwsh -command '.\install_modules.ps1' 17 | pre_build: 18 | commands: 19 | - pwsh -command '$PSVersionTable' 20 | # uncomment the line below to explore what modules/variables/env variables are available in the build image 21 | # - pwsh -command 'Get-Module -ListAvailable; (Get-Variable).GetEnumerator() | Sort-Object Name | Out-String; (Get-ChildItem env:*).GetEnumerator() | Sort-Object Name | Out-String' 22 | build: 23 | commands: 24 | - pwsh -command 'Invoke-Build -File .\src\PSGalleryExplorer.build.ps1' 25 | artifacts: 26 | files: 27 | - '**/*' 28 | base-directory: 'src\Archive' 29 | reports: 30 | PesterTestReport: 31 | files: 32 | - '**/*' 33 | file-format: NunitXml 34 | base-directory: 'src\Artifacts\testOutput' 35 | CodeCoverageReport: 36 | files: 37 | - 'src\Artifacts\ccReport\CodeCoverage.xml' 38 | file-format: 'JaCoCoXml' 39 | -------------------------------------------------------------------------------- /configure_aws_credential.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script is used in AWS CodeBuild to configure the default AWS Credentials for use by the AWS CLI and the AWS Powershell module. 4 | .DESCRIPTION 5 | By default, the AWS PowerShell Module does not know about looking up an AWS Container's credentials path, so this works around that issue. 6 | .NOTES 7 | This script enables AWSPowerShell cmdlets in your CodeBuild to interact with and access other AWS resources in your account. 8 | #> 9 | 'Configuring AWS credentials' 10 | 11 | ' - Retrieving temporary credentials from metadata' 12 | $uri = 'http://169.254.170.2{0}' -f $env:AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 13 | $sts = Invoke-RestMethod -UseBasicParsing -Uri $uri 14 | 15 | ' - Setting default AWS Credential' 16 | $credentialsFile = "$env:HOME\.aws\credentials" 17 | $null = New-Item -Path $credentialsFile -Force 18 | 19 | '[default]' | Out-File -FilePath $credentialsFile -Append 20 | 'aws_access_key_id={0}' -f $sts.AccessKeyId | Out-File -FilePath $credentialsFile -Append 21 | 'aws_secret_access_key={0}' -f $sts.SecretAccessKey | Out-File -FilePath $credentialsFile -Append 22 | 'aws_session_token={0}' -f $sts.Token | Out-File -FilePath $credentialsFile -Append 23 | 24 | ' - Setting default AWS Region' 25 | 'region={0}' -f $env:AWS_DEFAULT_REGION | Out-File -FilePath $credentialsFile -Append 26 | 27 | ' - AWS credentials configured' 28 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [2.5.4] 9 | 10 | - Module Changes 11 | - Updated links in `psd1` manifest 12 | - Added links to inline help for all functions 13 | - Build Updates 14 | - Adjusted metric dashboard arrangement and sizing + added alarm status 15 | - adjusted appveyor build 16 | - Misc 17 | - Updated CONTRIBUTING guidelines 18 | - Updated `extensions.json` with recommended extensions for working with this repo 19 | - Updated PSGalleryExplorer logo and icons 20 | 21 | ## [2.5.2] 22 | 23 | - Module Changes 24 | - `Confirm-XMLDataSet` now evaluates `LastWriteTime` instead of `CreationTime` to determine cache freshness requirements 25 | - Added additional corps to list 26 | - Build Updates 27 | - AWS Deployment Updates 28 | - Updated CodeBuild containers from `aws/codebuild/standard:6.0` to `aws/codebuild/standard:7.0` 29 | - Changed alarm for cache data to reference correct SNS topic for alerts 30 | - Added CloudWatch dashboard for data cache age metric 31 | - Misc 32 | - Added metric dashboards to docs section 33 | 34 | ## [2.5.0] 35 | 36 | - Module Updates 37 | - Added support for usage of a metadata file to better determine if data is current 38 | - Added `Confirm-MetadataUpdate` private function 39 | - Updated logic in `Confirm-XMLDataSet` and `Invoke-XMLDataCheck` 40 | - Removed private function `Get-XMLDataSet` for new private function `Get-RemoteFile` 41 | - Convert `1.5.0` or higher now required 42 | - Added the `InsightView` parameter to both `Find-ModuleByCommand` and `Find-PSGModule` 43 | - This provides a new results view that focuses on community insights 44 | - Build Updates 45 | - SSM Task now copies metadata file 46 | - InvokeBuild bumped from `5.10.2` to `5.10.3` 47 | - Convert bumped from `1.2.0` to `1.5.0` 48 | - Removed all test case uses of `Assert-MockCalled` 49 | - Improved test formatting 50 | - Updated test example references 51 | - Added additional integration tests for new properties 52 | 53 | ## [2.1.0] 54 | 55 | - Module Updates 56 | - New function: `Find-ModuleByCommand` 57 | 58 | ## [2.0.0] - *Breaking Changes Introduced* 59 | 60 | - Module Updates 61 | - **New CDN Endpoint** - ***Breaking Change*** 62 | - PSGalleryExplorer has migrated to a new CDN endpoint for serving repository information. As a result, the old endpoint has been decommissioned. 63 | - *What does this mean?* 64 | - Older versions of PSGalleryExplorer (prior to `v2.0.0`) will no longer be able to fetch module project information due to the new CDN endpoint. If you're using an older version, please upgrade to v2.0.0 or later to continue using PSGalleryExplorer. 65 | - Convert `1.2.0` or higher now required 66 | - Added additional corps to list 67 | - Added additional regulars to list 68 | - Build Updates: 69 | - Moved from a fully serverless PowerShell lambda architecture to a hybrid SSM execution architecture. 70 | - Added integration for Read the Docs 71 | - Moved `CHANGELOG.md` from `.github` directory to `docs` directory 72 | - Updated VSCode `tasks.json` 73 | - Added a `SECURITY.md` file for the project 74 | - All Infra/Infrastructure references changed to Integration 75 | - CI/CD Changes: 76 | - Pester bumped from `5.3.1` to `5.4.0` 77 | - InvokeBuild bumped from `5.9.7` to `5.10.2` 78 | - PSScriptAnalyzer bumped from `1.20.0` to `1.21.0` 79 | - Convert bumped from `0.6.0` to `1.2.0` 80 | - Switched AWS module install to use PSGallery 81 | - Updated CodeBuild Linux image from `aws/codebuild/standard:5.0` to `aws/codebuild/standard:6.0` 82 | - Updated CodeBuild Windows image from `aws/codebuild/windows-base:2019-1.0` to `aws/codebuild/windows-base:2019-2.0` 83 | - Added log retention groups to all CodeBuild projects 84 | 85 | ## [1.0.2] 86 | 87 | - Added additional corps to list 88 | - Added additional regulars to list 89 | - Changed column output for module name from 15 to 20 characters 90 | 91 | ## [1.0.0] 92 | 93 | - Module Updates 94 | - Module manifest 95 | - Added additional tags 96 | - Bumped Convert requirement to `0.6.0` 97 | - Separated Pester unit tests into separate test folders 98 | - Added support for Pester 5 99 | - Minor formatting changes to most functions 100 | - Build Updates: 101 | - Refreshed CodeBuild module build process 102 | - Minor updates to AWS CodeBuild buildspec files 103 | - pwsh_windows updated to utilize native pwsh using dotnet 3.1 104 | - refreshed installed modules to latest available versions 105 | - Appveyor MacOS now produces artifacts 106 | - Updated PSGalleryExplorer.build file to align with latest bug fixes 107 | - Updated CodeBuild images to latest available 108 | - Added support for using new main branch instead of master 109 | - Updated CodeBuild image from `aws/codebuild/amazonlinux2-x86_64-standard:3.0` to `aws/codebuild/standard:5.0` 110 | - Added tagging to Lambda log groups 111 | - ***Updated all lambdas from `.NET 3.1` to `.NET 6`*** 112 | - Bumped all PowerShell module versions to latest version 113 | - Added Pester 5 support 114 | - XML file generation is now sent to cloudfront 115 | - Added cloudfront logging 116 | - Added additional monitoring and alarms 117 | - Minor updates to VSCode settings/tasks/extension files 118 | 119 | ## [0.8.7] 120 | 121 | - Added support for including wildcards in ByName `Find-PSGModule -ByName Posh*` 122 | - Added support for returning all modules `Find-PSGModule` 123 | 124 | ## [0.8.5] 125 | 126 | - Module Updates: 127 | - **Support has been added to include information for projects hosted on GitLab** 128 | - Additional support for other repo locations is planned. As such several references in this module to GitHub have been replaced with Repo or repository. 129 | - **ByGitHubInfo parameter has been replaced with ByRepoInfo parameter** 130 | - **ByRecentUpdate parameter options have changed** 131 | - Previous: GalleryUpdate, GitUpdate 132 | - New: GalleryUpdate, RepoUpdate 133 | - **Repository issues is now a returned parameter in all queries** 134 | - **ByRepoInfo** now has a new parameter choice set: 135 | - StarCount, Forks, Issues, Subscribers 136 | - Added additional corps to corp list 137 | - Minor formatting changes to psd1 manifest 138 | - ByName selection now always includes all modules 139 | - The default output has been adjusted: 140 | - Previous: Name, Downloads, GitStar, GitFork, GitSub, GitWatch, Description 141 | - New: Name, Downloads, Star, Fork, Issues, Sub, Description 142 | - All data parameters are still available in the complete object return 143 | - Build Updates: 144 | - Added CodePipeline capability to deploy Serverless PSGallery solution through code 145 | - PSGallery lambda changes: 146 | - Updated to latest versions of used modules 147 | - A few bug fixes for not being able to properly craft URI 148 | - Added GitLab query capability 149 | - Added env variables to comments section 150 | - Refreshed CodeBuild module build process 151 | - Added test reporting capability 152 | - Updated linux CB to latest image and dotnet 3.1 153 | - Updated windows images to Server 2019 154 | - pwsh core build now uses PowerShell 7.0.3 instead of 7.0.0 155 | - refreshed installed modules to latest available versions 156 | 157 | ## [3/13/2020] 158 | 159 | No Version Change 160 | 161 | - Build/dev improvements 162 | - Bumped module versions to latest available 163 | - Switched Windows Build container to use PowerShell 7 instead of PowerShell 7 preview 164 | - Updated tasks.json to have better integration with InvokeBuild 165 | 166 | ## [0.8.0] 167 | 168 | ### Added 169 | 170 | - Initial release. 171 | -------------------------------------------------------------------------------- /docs/Find-ModuleByCommand.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSGalleryExplorer-help.xml 3 | Module Name: PSGalleryExplorer 4 | online version: https://psgalleryexplorer.readthedocs.io/en/latest/Find-ModuleByCommand/ 5 | schema: 2.0.0 6 | --- 7 | 8 | # Find-ModuleByCommand 9 | 10 | ## SYNOPSIS 11 | Searches for modules that contain a specific command or cmdlet name. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Find-ModuleByCommand [-CommandName] [-InsightView] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | The Find-ModuleByCommand cmdlet searches for modules on the PowerShell Gallery that contain a specified command or cmdlet name. 21 | The cmdlet returns a list of modules that include the command or cmdlet, along with key metrics and information about the module. 22 | This cmdlet is useful when you need to quickly find a module that includes a particular command or cmdlet, without having to install or download the module first. 23 | 24 | ## EXAMPLES 25 | 26 | ### EXAMPLE 1 27 | ``` 28 | Find-ModuleByCommand -CommandName New-ModuleProject 29 | ``` 30 | 31 | Returns a list of modules that contain the command New-ModuleProject 32 | 33 | ### EXAMPLE 2 34 | ``` 35 | Find-ModuleByCommand -CommandName 'Send-TelegramTextMessage' 36 | ``` 37 | 38 | Returns a list of modules that contain the command Send-TelegramTextMessage 39 | 40 | ### EXAMPLE 3 41 | ``` 42 | Find-ModuleByCommand -CommandName 'Send-TelegramTextMessage' -InsightView 43 | ``` 44 | 45 | Returns a list of modules that contain the command Send-TelegramTextMessage, with focused community insights about the module 46 | 47 | ## PARAMETERS 48 | 49 | ### -CommandName 50 | Specifies the command name to search for 51 | 52 | ```yaml 53 | Type: String 54 | Parameter Sets: (All) 55 | Aliases: 56 | 57 | Required: True 58 | Position: 1 59 | Default value: None 60 | Accept pipeline input: False 61 | Accept wildcard characters: False 62 | ``` 63 | 64 | ### -InsightView 65 | Output focuses on additional insights available through PSGalleryExplorer. 66 | This includes the module's size and file count, as well as repository metrics like stars, forks, and last repo update date 67 | 68 | ```yaml 69 | Type: SwitchParameter 70 | Parameter Sets: (All) 71 | Aliases: 72 | 73 | Required: False 74 | Position: Named 75 | Default value: False 76 | Accept pipeline input: False 77 | Accept wildcard characters: False 78 | ``` 79 | 80 | ### CommonParameters 81 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. 82 | For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 83 | 84 | ## INPUTS 85 | 86 | ## OUTPUTS 87 | 88 | ### PSGEFormat 89 | ## NOTES 90 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 91 | 92 | ## RELATED LINKS 93 | 94 | [https://psgalleryexplorer.readthedocs.io/en/latest/Find-ModuleByCommand/](https://psgalleryexplorer.readthedocs.io/en/latest/Find-ModuleByCommand/) 95 | 96 | -------------------------------------------------------------------------------- /docs/Find-PSGModule.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSGalleryExplorer-help.xml 3 | Module Name: PSGalleryExplorer 4 | online version: https://psgalleryexplorer.readthedocs.io/en/latest/Find-PSGModule/ 5 | schema: 2.0.0 6 | --- 7 | 8 | # Find-PSGModule 9 | 10 | ## SYNOPSIS 11 | Finds PowerShell Gallery module(s) that match specified criteria. 12 | 13 | ## SYNTAX 14 | 15 | ### none (Default) 16 | ``` 17 | Find-PSGModule [-IncludeCorps] [-IncludeRegulars] [-NumberToReturn ] [-InsightView] [] 18 | ``` 19 | 20 | ### GalleryDownloads 21 | ``` 22 | Find-PSGModule [-ByDownloads] [-ByRandom] [-IncludeCorps] [-IncludeRegulars] [-NumberToReturn ] 23 | [-InsightView] [] 24 | ``` 25 | 26 | ### Repo 27 | ``` 28 | Find-PSGModule [-ByRepoInfo ] [-IncludeCorps] [-IncludeRegulars] [-NumberToReturn ] 29 | [-InsightView] [] 30 | ``` 31 | 32 | ### Update 33 | ``` 34 | Find-PSGModule [-ByRecentUpdate ] [-IncludeCorps] [-IncludeRegulars] [-NumberToReturn ] 35 | [-InsightView] [] 36 | ``` 37 | 38 | ### Names 39 | ``` 40 | Find-PSGModule [-ByName ] [-IncludeCorps] [-IncludeRegulars] [-NumberToReturn ] [-InsightView] 41 | [] 42 | ``` 43 | 44 | ### Tags 45 | ``` 46 | Find-PSGModule [-ByTag ] [-IncludeCorps] [-IncludeRegulars] [-NumberToReturn ] [-InsightView] 47 | [] 48 | ``` 49 | 50 | ## DESCRIPTION 51 | Searches PowerShell Gallery for modules and their associated project repositories. 52 | Results are returned based on provided criteria. 53 | By default, more common/popular modules and modules made by corporations are excluded. 54 | This is to aid in discovery of other modules. 55 | Popular modules and corporation modules can be included in results by specifying the necessary parameter switches. 56 | 35 module results are returned by default unless the NumberToReturn parameter is used. 57 | 58 | ## EXAMPLES 59 | 60 | ### EXAMPLE 1 61 | ``` 62 | Find-PSGModule -ByDownloads 63 | ``` 64 | 65 | Returns up to 35 modules based on number of PowerShell Gallery downloads. 66 | 67 | ### EXAMPLE 2 68 | ``` 69 | Find-PSGModule -ByDownloads -IncludeRegulars 70 | ``` 71 | 72 | Returns up to 35 modules based on number of PowerShell Gallery downloads including more popular modules. 73 | 74 | ### EXAMPLE 3 75 | ``` 76 | Find-PSGModule -ByDownloads -IncludeCorps -IncludeRegulars -NumberToReturn 50 77 | ``` 78 | 79 | Returns up to 50 modules based on number of PowerShell Gallery downloads including more popular downloads, and modules made by corporations. 80 | 81 | ### EXAMPLE 4 82 | ``` 83 | Find-PSGModule -ByRepoInfo StarCount 84 | ``` 85 | 86 | Returns up to 35 modules based on number of stars the project's repository has. 87 | 88 | ### EXAMPLE 5 89 | ``` 90 | Find-PSGModule -ByRepoInfo Forks 91 | ``` 92 | 93 | Returns up to 35 modules based on number of forks the project's repository has. 94 | 95 | ### EXAMPLE 6 96 | ``` 97 | Find-PSGModule -ByRepoInfo Issues 98 | ``` 99 | 100 | Returns up to 35 modules based on number of issues the project's repository has. 101 | 102 | ### EXAMPLE 7 103 | ``` 104 | Find-PSGModule -ByRepoInfo Subscribers 105 | ``` 106 | 107 | Returns up to 35 modules based on number of subscribers the project's repository has. 108 | 109 | ### EXAMPLE 8 110 | ``` 111 | Find-PSGModule -ByRecentUpdate GalleryUpdate 112 | ``` 113 | 114 | Returns up to 35 modules based on their most recent PowerShell Gallery update. 115 | 116 | ### EXAMPLE 9 117 | ``` 118 | Find-PSGModule -ByRecentUpdate RepoUpdate 119 | ``` 120 | 121 | Returns up to 35 modules based on recent updates to their associated repository. 122 | 123 | ### EXAMPLE 10 124 | ``` 125 | Find-PSGModule -ByRandom 126 | ``` 127 | 128 | Returns up to 35 modules randomly 129 | 130 | ### EXAMPLE 11 131 | ``` 132 | Find-PSGModule -ByName 'PoshGram' 133 | ``` 134 | 135 | Returns module that equals the provided name, if found. 136 | 137 | ### EXAMPLE 12 138 | ``` 139 | Find-PSGModule -ByName 'Posh*' 140 | ``` 141 | 142 | Returns all modules that match the wild card provided name, if found. 143 | 144 | ### EXAMPLE 13 145 | ``` 146 | Find-PSGModule -ByTag Telegram 147 | ``` 148 | 149 | Returns up to 35 modules that contain the tag: Telegram. 150 | 151 | ### EXAMPLE 14 152 | ``` 153 | Find-PSGModule -ByTag Telegram -IncludeCorps -IncludeRegulars -NumberToReturn 100 154 | ``` 155 | 156 | Returns up to 100 modules that contains the tag: Telegram, including more popular modules and modules made by corporations. 157 | 158 | ### EXAMPLE 15 159 | ``` 160 | $results = Find-PSGModule -ByRepoInfo Watchers -IncludeCorps -IncludeRegulars -NumberToReturn 40 161 | $results | Format-List 162 | ``` 163 | 164 | Returns up to 40 modules based on number of module project repository watchers. 165 | It includes more popular modules as well as modules made by corporations. 166 | A list of results is displayed. 167 | 168 | ### EXAMPLE 16 169 | ``` 170 | Find-PSGModule 171 | ``` 172 | 173 | Returns all non-corp/non-regular modules 174 | 175 | ### EXAMPLE 17 176 | ``` 177 | Find-PSGModule -IncludeCorps -IncludeRegulars 178 | ``` 179 | 180 | Returns all modules 181 | 182 | ### EXAMPLE 18 183 | ``` 184 | Find-PSGModule -ByTag module -InsightView 185 | ``` 186 | 187 | Returns up to 35 modules that contain the tag: module. 188 | The output focuses on additional insights available through PSGalleryExplorer. 189 | This includes the module's size and file count, as well as repository metrics like stars, forks, and last repo update date. 190 | 191 | ## PARAMETERS 192 | 193 | ### -ByDownloads 194 | Find modules by number of PowerShell Gallery Downloads 195 | 196 | ```yaml 197 | Type: SwitchParameter 198 | Parameter Sets: GalleryDownloads 199 | Aliases: 200 | 201 | Required: False 202 | Position: Named 203 | Default value: False 204 | Accept pipeline input: False 205 | Accept wildcard characters: False 206 | ``` 207 | 208 | ### -ByRepoInfo 209 | Find modules based on various project repository metrics 210 | 211 | ```yaml 212 | Type: String 213 | Parameter Sets: Repo 214 | Aliases: 215 | 216 | Required: False 217 | Position: Named 218 | Default value: None 219 | Accept pipeline input: False 220 | Accept wildcard characters: False 221 | ``` 222 | 223 | ### -ByRecentUpdate 224 | Find modules based on recent updated to PowerShell Gallery or associated repository 225 | 226 | ```yaml 227 | Type: String 228 | Parameter Sets: Update 229 | Aliases: 230 | 231 | Required: False 232 | Position: Named 233 | Default value: None 234 | Accept pipeline input: False 235 | Accept wildcard characters: False 236 | ``` 237 | 238 | ### -ByRandom 239 | Find modules randomly from the PowerShell Gallery 240 | 241 | ```yaml 242 | Type: SwitchParameter 243 | Parameter Sets: GalleryDownloads 244 | Aliases: 245 | 246 | Required: False 247 | Position: Named 248 | Default value: False 249 | Accept pipeline input: False 250 | Accept wildcard characters: False 251 | ``` 252 | 253 | ### -ByName 254 | Find module by module name 255 | 256 | ```yaml 257 | Type: String 258 | Parameter Sets: Names 259 | Aliases: 260 | 261 | Required: False 262 | Position: Named 263 | Default value: None 264 | Accept pipeline input: False 265 | Accept wildcard characters: False 266 | ``` 267 | 268 | ### -ByTag 269 | Find modules by tag 270 | 271 | ```yaml 272 | Type: String 273 | Parameter Sets: Tags 274 | Aliases: 275 | 276 | Required: False 277 | Position: Named 278 | Default value: None 279 | Accept pipeline input: False 280 | Accept wildcard characters: False 281 | ``` 282 | 283 | ### -IncludeCorps 284 | Include modules written by corporations in results 285 | 286 | ```yaml 287 | Type: SwitchParameter 288 | Parameter Sets: (All) 289 | Aliases: 290 | 291 | Required: False 292 | Position: Named 293 | Default value: False 294 | Accept pipeline input: False 295 | Accept wildcard characters: False 296 | ``` 297 | 298 | ### -IncludeRegulars 299 | Include modules that are well known in results 300 | 301 | ```yaml 302 | Type: SwitchParameter 303 | Parameter Sets: (All) 304 | Aliases: 305 | 306 | Required: False 307 | Position: Named 308 | Default value: False 309 | Accept pipeline input: False 310 | Accept wildcard characters: False 311 | ``` 312 | 313 | ### -NumberToReturn 314 | Max number of modules to return 315 | 316 | ```yaml 317 | Type: Int32 318 | Parameter Sets: (All) 319 | Aliases: 320 | 321 | Required: False 322 | Position: Named 323 | Default value: 35 324 | Accept pipeline input: False 325 | Accept wildcard characters: False 326 | ``` 327 | 328 | ### -InsightView 329 | Output focuses on additional insights available through PSGalleryExplorer. 330 | This includes the module's size and file count, as well as repository metrics like stars, forks, and last repo update date 331 | 332 | ```yaml 333 | Type: SwitchParameter 334 | Parameter Sets: (All) 335 | Aliases: 336 | 337 | Required: False 338 | Position: Named 339 | Default value: False 340 | Accept pipeline input: False 341 | Accept wildcard characters: False 342 | ``` 343 | 344 | ### CommonParameters 345 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. 346 | For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 347 | 348 | ## INPUTS 349 | 350 | ## OUTPUTS 351 | 352 | ### PSGEFormat 353 | ## NOTES 354 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 355 | 356 | ## RELATED LINKS 357 | 358 | [https://psgalleryexplorer.readthedocs.io/en/latest/Find-PSGModule/](https://psgalleryexplorer.readthedocs.io/en/latest/Find-PSGModule/) 359 | 360 | -------------------------------------------------------------------------------- /docs/PSGalleryExplorer-Data_Collection.md: -------------------------------------------------------------------------------- 1 | # PSGalleryExplorer - Data Component 2 | 3 | ## Overview 4 | 5 | One key feature of PSGalleryExplorer is the inclusion of associated repository information for each module. 6 | 7 | For several years PSGalleryExplorer leveraged a [PowerShell serverless model](assets/Serverless_PowerShell_DataPull.png) for data collection. However, as the number of modules continued to grow, this was unable to process the data set in a time efficient manner. 8 | 9 | Today, a fully PowerShell hybrid solution is deployed to continually collect and update repository information for PSGalleryExplorer's use. 10 | 11 | ## Deployment Stack 12 | 13 | - [Cloudformation for Serverless deployment](../CloudFormation/PSGalleryExplorer/) 14 | 15 | ## Design Diagram 16 | 17 | ![PSGalleryExplorer Hybrid SSM repository data scrape](assets/PSGalleryExplorer_datapull.png) 18 | 19 | ## Outline 20 | 21 | 1. A Hybrid worker configured via [AWS Systems Manager](https://aws.amazon.com/systems-manager/) is configured with a weekly scheduled task. 22 | - This task: 23 | - Downloads all current modules from the PSGallery 24 | - Identifies modules that have public repositories 25 | - Queries [GitHub](https://github.com/), [GitLab](https://gitlab.com), and [Bitbucket](https://bitbucket.org) to retrieve project information 26 | - Combines data sets together to one final data set. 27 | 1. An [AWS Systems Manager Maintenance Window](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-maintenance.html) task is set up to retrieve the data set and publish it to [Amazon CloudFront](https://aws.amazon.com/cloudfront/) 28 | 1. Users of PSGalleryExplorer can quickly download the refreshed data set worldwide when running searches 29 | 30 | You can see additional metric data of data cache age, and cache downloads on the [PSGalleryExplorer Metrics](PSGalleryExplorer-Metrics.md) page. 31 | -------------------------------------------------------------------------------- /docs/PSGalleryExplorer-FAQ.md: -------------------------------------------------------------------------------- 1 | # PSGalleryExplorer - FAQ 2 | 3 | ## FAQs 4 | 5 | ### How current is the GitHub info that is included with PSGallery Explorer? 6 | 7 | There can be up to a 1 week delay in corresponding PowerShell GitHub project data. This is solely due to cost. The [Serverless PowerShell solution](PowerShell_Serverless.md) in place cost compute time. As such, it's currently set to refresh approximately once per week. 8 | 9 | ### Why are common modules and corporate modules excluded by default? 10 | 11 | By default, PSGalleryExplorer excludes common modules and corporate modules from search results. These modules tend to dominate the metrics by a wide margin, making it challenging to discover new or trending modules. However, it is easy to include these modules in your search results by using the appropriate parameters. 12 | 13 | If you feel like a module should be added/excluded please open an issue for discussion. 14 | -------------------------------------------------------------------------------- /docs/PSGalleryExplorer-Metrics.md: -------------------------------------------------------------------------------- 1 | # PSGalleryExplorer Metrics 2 | 3 | ## Metric Dashboards 4 | 5 | Up-to-date visualizations representing the age of the data cache and the frequency of download requests. 6 | 7 | 11 | -------------------------------------------------------------------------------- /docs/PSGalleryExplorer.md: -------------------------------------------------------------------------------- 1 | --- 2 | Module Name: PSGalleryExplorer 3 | Module Guid: e9252e8e-2073-4084-9562-cf60ad84603d 4 | Download Help Link: NA 5 | Help Version: 2.5.4 6 | Locale: en-US 7 | --- 8 | 9 | # PSGalleryExplorer Module 10 | ## Description 11 | Search, explore, and discover PowerShell Gallery modules based on various criteria. 12 | 13 | ## PSGalleryExplorer Cmdlets 14 | ### [Find-ModuleByCommand](Find-ModuleByCommand.md) 15 | Searches for modules that contain a specific command or cmdlet name. 16 | 17 | ### [Find-PSGModule](Find-PSGModule.md) 18 | Finds PowerShell Gallery module(s) that match specified criteria. 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/assets/PSGalleryExplorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/PSGalleryExplorer.png -------------------------------------------------------------------------------- /docs/assets/PSGalleryExplorerIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/PSGalleryExplorerIcon.png -------------------------------------------------------------------------------- /docs/assets/PSGalleryExplorer_datapull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/PSGalleryExplorer_datapull.png -------------------------------------------------------------------------------- /docs/assets/PSGalleryExplorer_favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/PSGalleryExplorer_favicon-32x32.png -------------------------------------------------------------------------------- /docs/assets/Serverless_PowerShell_DataPull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/Serverless_PowerShell_DataPull.png -------------------------------------------------------------------------------- /docs/assets/favicon_io/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/favicon_io/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/assets/favicon_io/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/favicon_io/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/assets/favicon_io/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/favicon_io/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/assets/favicon_io/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/favicon_io/favicon-16x16.png -------------------------------------------------------------------------------- /docs/assets/favicon_io/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/favicon_io/favicon.ico -------------------------------------------------------------------------------- /docs/assets/favicon_io/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /docs/assets/psgalleryexplorer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/docs/assets/psgalleryexplorer.gif -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # PSGalleryExplorer 2 | 3 | [![Minimum Supported PowerShell Version](https://img.shields.io/badge/PowerShell-5.1+-blue.svg)](https://github.com/PowerShell/PowerShell) [![PowerShell Gallery][psgallery-img]][psgallery-site] ![Cross Platform](https://img.shields.io/badge/platform-windows%20%7C%20macos%20%7C%20linux-lightgrey) [![License][license-badge]](LICENSE) [![Documentation Status](https://readthedocs.org/projects/psgalleryexplorer/badge/?version=latest)](https://psgalleryexplorer.readthedocs.io/en/latest/?badge=latest) 4 | 5 | [psgallery-img]: https://img.shields.io/powershellgallery/dt/PSGalleryExplorer?label=Powershell%20Gallery&logo=powershell 6 | [psgallery-site]: https://www.powershellgallery.com/packages/PSGalleryExplorer 7 | [psgallery-v1]: https://www.powershellgallery.com/packages/PSGalleryExplorer/0.8.0 8 | [license-badge]: https://img.shields.io/github/license/techthoughts2/PSGalleryExplorer 9 | 10 |

11 | PSGalleryExplorer Logo 12 |

13 | 14 | ## What is PSGalleryExplorer? 15 | 16 | PSGalleryExplorer is a PowerShell module that extends the search functionality of the PowerShell Gallery by providing additional project information about modules. This enables you to search, explore, and discover PowerShell Gallery modules based on additional criteria. 17 | 18 | PSGalleryExplorer is not intended to replace `Find-Module`. Rather, it complements it by providing a means to discover modules and gain insights into their associated project repositories. PSGalleryExplorer enables you to expand your exploration of the PowerShell Gallery, identifying new, trending, and modules with heavy community involvement. 19 | 20 | ## Why PSGalleryExplorer? 21 | 22 | To aid in the discoverability of modules in the PowerShell Gallery. 23 | 24 | The current PowerShell Gallery search options are primarily limited to module name, and tags. CI/CD processes also inflate the download numbers of many modules on the gallery. This makes it challenging to get a sense of new or trending modules, or modules that the community is engaging with. 25 | 26 | This project aims to increase the discoverability of modules on the PowerShell Gallery and encourage module exploration. 27 | 28 | For example, when exploring what is available for adding message functionality, PSGalleryExplorer provides repo statistics on the associated projects (when available), such as star count and issues, giving you a better understanding of the module's community involvement and overall health. 29 | 30 | To further enhance the discoverability of modules, PSGalleryExplorer now includes the `Find-ModuleByCommand` function, which allows users to search for modules based on a specific command, even if the module is not installed locally. This feature provides an additional means to explore modules in the PowerShell Gallery and identify modules that contain specific commands that you may be interested in. 31 | 32 | `Find-Module` Example: 33 | 34 | ```powershell 35 | Find-Module -Tag message 36 | 37 | Version Name Repository Description 38 | ------- ---- ---------- ----------- 39 | 1.0.6 PSSlack PSGallery PowerShell module for the Slack API 40 | 2.3.0 PoshGram PSGallery PoshGram provides functionality to send various message t… 41 | 0.3.5 PSRabbitMq PSGallery Send and receive messages using a RabbitMQ server 42 | 1.1.0 Environment PSGallery Provides Trace-Message, and functions for working with En… 43 | 0.6.0 GitUtils PSGallery A set of functions for git tasks 44 | 0.0.1 Send-Message PSGallery Show popup message box on local or remote computers 45 | 0.0.8 PSRyver PSGallery Community PowerShell module for the Ryver API 46 | 1.0.0.0 MessageBox PSGallery Easy to use to create a popup message box 47 | ``` 48 | 49 | PSGalleryExplorer Example: 50 | 51 | ```powershell 52 | Find-PSGModule -ByTag message -IncludeCorps -IncludeRegulars 53 | 54 | Name Downloads Star Fork Issues Sub Description 55 | ---- --------- ---- ---- ------ --- ----------- 56 | PSSlack 10390604 255 72 41 29 PowerShell module for the Slack API 57 | PoshGram 81268 115 11 4 10 PoshGram provides functionality to send various message types to a specif… 58 | PSRabbitMq 21769 42 28 2 9 Send and receive messages using a RabbitMQ server 59 | Environment 2091 24 2 0 3 Provides Trace-Message, and functions for working with Environment and Pa… 60 | GitUtils 783 A set of functions for git tasks 61 | PSRyver 449 0 0 0 2 Community PowerShell module for the Ryver API 62 | MessageBox 439 Easy to use to create a popup message box 63 | Send-Message 411 Show popup message box on local or remote computers 64 | ``` 65 | 66 | ## Getting Started 67 | 68 | ### Installation 69 | 70 | ```powershell 71 | # Install PSGalleryExplorer from the PowerShell Gallery 72 | Install-Module -Name PSGalleryExplorer -Repository PSGallery -Scope CurrentUser 73 | ``` 74 | 75 | ### Quick start 76 | 77 | ```powershell 78 | #------------------------------------------------------------------------------------------------ 79 | # import the PSGalleryExplorer module 80 | Import-Module -Name "PSGalleryExplorer" 81 | #------------------------------------------------------------------------------------------------ 82 | # discover module info by tag 83 | Find-PSGModule -ByTag Telegram 84 | #------------------------------------------------------------------------------------------------ 85 | # discover PowerShell modules by # of Gallery Downloads 86 | Find-PSGModule -ByDownloads 87 | #------------------------------------------------------------------------------------------------ 88 | # discover the most recently updated modules on repo 89 | Find-PSGModule -ByRecentUpdate RepoUpdate 90 | #------------------------------------------------------------------------------------------------ 91 | # discover the most recently updated modules on the PowerShell Gallery 92 | Find-PSGModule -ByRecentUpdate GalleryUpdate 93 | #------------------------------------------------------------------------------------------------ 94 | # discover PowerShell modules by # of Gallery Downloads 95 | # include corporate modules and common/popular modules in results 96 | # return top 50 97 | Find-PSGModule -ByDownloads -IncludeCorps -IncludeRegulars -NumberToReturn 50 98 | #------------------------------------------------------------------------------------------------ 99 | # discover PowerShell modules by # of repo project stars 100 | Find-PSGModule -ByRepoInfo StarCount 101 | #------------------------------------------------------------------------------------------------ 102 | # discover PowerShell modules that could possibly use some help 103 | Find-PSGModule -ByRepoInfo Issues 104 | #------------------------------------------------------------------------------------------------ 105 | # discover PowerShell modules by # of repo project subscribers 106 | Find-PSGModule -ByRepoInfo Subscribers 107 | #------------------------------------------------------------------------------------------------ 108 | # discover a set of random modules 109 | Find-PSGModule -ByRandom 110 | #------------------------------------------------------------------------------------------------ 111 | # discover module info by name 112 | Find-PSGModule -ByName 'PoshGram' 113 | #------------------------------------------------------------------------------------------------ 114 | # Returns a list of modules that contain the command Send-TelegramTextMessage 115 | Find-ModuleByCommand -CommandName 'Send-TelegramTextMessage' 116 | #------------------------------------------------------------------------------------------------ 117 | ``` 118 | 119 | ## How PSGalleryExplorer Works 120 | 121 | PSGalleryExplorer uses a workflow to collect and serve information about module repositories. It scrapes repository data for modules that have public repositories, then aggregates and includes that data in an easy-to-read format. 122 | 123 | ## Features 124 | 125 | - Fully cross-platform and can be run on Windows, Linux, and macOS 126 | - Discover modules based on various criteria such as number of downloads, stars, forks, and more 127 | - Get insights into the community health of a module's repository, including information about open issues, license, and last updated date 128 | - Identify modules that are actively being developed by filtering based on their most recent repository update date. 129 | - Compliments existing tools like `Find-Module` to provide another way to explore modules on the PowerShell Gallery. 130 | - Identify up-and-coming or trending modules by comparing search results including and excluding popular and corporate modules 131 | - PSGalleryExplorer provides a detailed, informative output of module results to help you quickly identify prime candidates for further exploration. 132 | - `Find-ModuleByCommand` allows users to search for modules based on a specific command name, even if the module is not installed locally, providing a quick and easy way to locate modules containing the desired functionality. 133 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # https://github.com/readthedocs-examples/example-mkdocs-basic/blob/main/docs/requirements.txt 2 | # requirements.txt 3 | jinja2==3.0.3 4 | mkdocs>=1.4.2 5 | mkdocs-material>=9.0.12 #https://github.com/squidfunk/mkdocs-material 6 | pygments>=2.14.0 7 | # mdx_truly_sane_lists 8 | -------------------------------------------------------------------------------- /install_modules.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script is used in AWS CodeBuild to install the required PowerShell Modules for the build process. 4 | .DESCRIPTION 5 | The version of PowerShell being run will be identified. This may vary depending on what type of build 6 | container you are running and if your buildspec is installing various versions of PowerShell. You will 7 | need to specify each module and version that is required for installation. You also need to specify 8 | which version of that module should be installed. Additionally, you will need to specify the S3 bucket 9 | location where that module currently resides, so that it can be downloaded and installed into the build 10 | container at runtime. This necessitates that you download and upload your required modules to S3 prior to 11 | the build being executed. 12 | .EXAMPLE 13 | Save-Module -Name Pester -RequiredVersion 4.4.5 -Path C:\RequiredModules 14 | Create an S3 bucket in your AWS account 15 | Zip the contents of the Pester Module up (when done properly the .psd1 of the module should be at the root of the zip) 16 | Name the ZIP file Pester_4.4.4 (adjust version as needed) unless you want to modify the logic below 17 | Upload the Pester Zip file up to S3 bucket you just created 18 | .NOTES 19 | AWSPowerShell / AWSPowerShell.NetCore module should be included in all CodeBuild projects and is included below 20 | Pester, InvokeBuild, PSScriptAnalyzer, platyPS will typically be required by all module builds 21 | which is why they are included in this build script. Adjust versions as needed. 22 | #> 23 | 24 | 25 | $galleryDownload = $true 26 | 27 | $ErrorActionPreference = 'Stop' 28 | $ProgressPreference = 'SilentlyContinue' 29 | $VerbosePreference = 'SilentlyContinue' 30 | 31 | # List of PowerShell Modules required for the build 32 | # The AWS PowerShell Modules are added below, based on the $PSEdition 33 | $modulesToInstall = [System.Collections.ArrayList]::new() 34 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 35 | ModuleName = 'Pester' 36 | ModuleVersion = '5.5.0' 37 | BucketName = 'PSGallery' 38 | KeyPrefix = '' 39 | })) 40 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 41 | ModuleName = 'InvokeBuild' 42 | ModuleVersion = '5.10.4' 43 | BucketName = 'PSGallery' 44 | KeyPrefix = '' 45 | })) 46 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 47 | ModuleName = 'PSScriptAnalyzer' 48 | ModuleVersion = '1.21.0' 49 | BucketName = 'PSGallery' 50 | KeyPrefix = '' 51 | })) 52 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 53 | ModuleName = 'platyPS' 54 | ModuleVersion = '0.12.0' 55 | BucketName = 'PSGallery' 56 | KeyPrefix = '' 57 | })) 58 | $null = $modulesToInstall.Add(([PSCustomObject]@{ 59 | ModuleName = 'Convert' 60 | ModuleVersion = '1.5.0' 61 | BucketName = 'PSGallery' 62 | KeyPrefix = '' 63 | })) 64 | 65 | if ($galleryDownload -eq $false) { 66 | 67 | $tempPath = [System.IO.Path]::GetTempPath() 68 | 69 | if ($PSVersionTable.Platform -eq 'Win32NT') { 70 | $moduleInstallPath = [System.IO.Path]::Combine($env:ProgramFiles, 'WindowsPowerShell', 'Modules') 71 | if ($PSEdition -eq 'Core') { 72 | $moduleInstallPath = [System.IO.Path]::Combine($env:ProgramFiles, 'PowerShell', 'Modules') 73 | # Add the AWSPowerShell.NetCore Module 74 | # $null = $modulesToInstall.Add(([PSCustomObject]@{ 75 | # ModuleName = 'AWSPowerShell.NetCore' 76 | # ModuleVersion = '3.3.604.0' 77 | # BucketName = 'ps-invoke-modules' 78 | # KeyPrefix = '' 79 | # })) 80 | } 81 | else { 82 | $moduleInstallPath = [System.IO.Path]::Combine($env:ProgramFiles, 'WindowsPowerShell', 'Modules') 83 | # Add the AWSPowerShell Module 84 | # $null = $modulesToInstall.Add(([PSCustomObject]@{ 85 | # ModuleName = 'AWSPowerShell' 86 | # ModuleVersion = '3.3.604.0' 87 | # BucketName = 'ps-invoke-modules' 88 | # KeyPrefix = '' 89 | # })) 90 | } 91 | } 92 | elseif ($PSVersionTable.Platform -eq 'Unix') { 93 | $moduleInstallPath = [System.IO.Path]::Combine('/', 'usr', 'local', 'share', 'powershell', 'Modules') 94 | 95 | # Add the AWSPowerShell.NetCore Module 96 | # $null = $modulesToInstall.Add(([PSCustomObject]@{ 97 | # ModuleName = 'AWSPowerShell.NetCore' 98 | # ModuleVersion = '3.3.604.0' 99 | # BucketName = 'ps-invoke-modules' 100 | # KeyPrefix = '' 101 | # })) 102 | } 103 | elseif ($PSEdition -eq 'Desktop') { 104 | $moduleInstallPath = [System.IO.Path]::Combine($env:ProgramFiles, 'WindowsPowerShell', 'Modules') 105 | # Add the AWSPowerShell Module 106 | # $null = $modulesToInstall.Add(([PSCustomObject]@{ 107 | # ModuleName = 'AWSPowerShell' 108 | # ModuleVersion = '3.3.604.0' 109 | # BucketName = 'ps-invoke-modules' 110 | # KeyPrefix = '' 111 | # })) 112 | } 113 | else { 114 | throw 'Unrecognized OS platform' 115 | } 116 | 117 | 'Installing PowerShell Modules' 118 | foreach ($module in $modulesToInstall) { 119 | ' - {0} {1}' -f $module.ModuleName, $module.ModuleVersion 120 | 121 | # Download file from S3 122 | $key = '{0}_{1}.zip' -f $module.ModuleName, $module.ModuleVersion 123 | $localFile = Join-Path -Path $tempPath -ChildPath $key 124 | 125 | # Download modules from S3 to using the AWS CLI 126 | #note: remove --quiet for more verbose output or if S3 download troubleshooting is needed 127 | $s3Uri = 's3://{0}/{1}{2}' -f $module.BucketName, $module.KeyPrefix, $key 128 | & aws s3 cp $s3Uri $localFile --quiet 129 | 130 | # Ensure the download worked 131 | if (-not(Test-Path -Path $localFile)) { 132 | $message = 'Failed to download {0}' -f $module.ModuleName 133 | " - $message" 134 | throw $message 135 | } 136 | 137 | # Create module path 138 | $modulePath = Join-Path -Path $moduleInstallPath -ChildPath $module.ModuleName 139 | $moduleVersionPath = Join-Path -Path $modulePath -ChildPath $module.ModuleVersion 140 | $null = New-Item -Path $modulePath -ItemType 'Directory' -Force 141 | $null = New-Item -Path $moduleVersionPath -ItemType 'Directory' -Force 142 | 143 | # Expand downloaded file 144 | Expand-Archive -Path $localFile -DestinationPath $moduleVersionPath -Force 145 | } 146 | } #if_GalleryDownload 147 | else { 148 | Get-PackageProvider -Name Nuget -ForceBootstrap | Out-Null 149 | 'Installing PowerShell Modules' 150 | Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted 151 | # $NuGetProvider = Get-PackageProvider -Name "NuGet" -ErrorAction SilentlyContinue 152 | # if ( -not $NugetProvider ) { 153 | # Install-PackageProvider -Name "NuGet" -Confirm:$false -Force -Verbose 154 | # } 155 | foreach ($module in $modulesToInstall) { 156 | $installSplat = @{ 157 | Name = $module.ModuleName 158 | RequiredVersion = $module.ModuleVersion 159 | Repository = 'PSGallery' 160 | SkipPublisherCheck = $true 161 | Force = $true 162 | ErrorAction = 'Stop' 163 | } 164 | try { 165 | Install-Module @installSplat 166 | Import-Module -Name $module.ModuleName -ErrorAction Stop 167 | ' - Successfully installed {0}' -f $module.ModuleName 168 | } 169 | catch { 170 | $message = 'Failed to install {0}' -f $module.ModuleName 171 | " - $message" 172 | throw 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /lambdafunctions/PowerShell/PubXMLMonitor/PubXMLMonitor.build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | An Invoke-Build Build file for an AWS PowerShell Lambda Function 4 | 5 | .DESCRIPTION 6 | This build file is configured for AWS CodeBuild builds, but will work locally as well. 7 | 8 | Build steps can include: 9 | - InstallDependencies 10 | - Clean 11 | - Analyze 12 | - Test 13 | - Build 14 | - Archive 15 | 16 | The default build will not call the 'InstallDependencies' task, but can be used if you want PSDepend 17 | to install pre-requisite modules. "Invoke-Build -Task InstallDependencies" 18 | 19 | This build will pull in configurations from the ".Settings.ps1" file as well, where users can 20 | more easily customize the build process if required. 21 | 22 | .EXAMPLE 23 | Invoke-Build 24 | 25 | This will perform the default build tasks: Clean, Analyze, Test, Build, Archive 26 | 27 | .EXAMPLE 28 | Invoke-Build -Task Analyze,Test 29 | 30 | This will perform only the Analyze and Test tasks. 31 | #> 32 | 33 | # Include: Settings 34 | $ScriptName = (Split-Path -Path $BuildFile -Leaf).Split('.')[0] 35 | . "./$ScriptName.settings.ps1" 36 | 37 | # Default Build 38 | task . Clean, ImportModules, Analyze, Build, Publish 39 | 40 | # Pre-build variables to configure 41 | Enter-Build { 42 | $script:LambdaName = (Split-Path -Path $BuildFile -Leaf).Split('.')[0] 43 | 44 | # Identify other required paths 45 | $script:LambdaSourcePath = $BuildRoot 46 | $script:LambdaScriptPath = Join-Path -Path $script:LambdaSourcePath -ChildPath "$ScriptName.ps1" 47 | $script:ArtifactsPath = Join-Path -Path $BuildRoot -ChildPath 'Artifacts' 48 | $script:ArtifactPackage = Join-Path -Path $script:ArtifactsPath -ChildPath "$ScriptName.zip" 49 | $script:PowerShellLambdaPath = Split-Path -Path $script:LambdaSourcePath 50 | $script:StagingPath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath 'LambdaStaging' 51 | 52 | if ([String]::IsNullOrWhiteSpace($env:CODEBUILD_SRC_DIR)) { 53 | $script:LambdaFunctionsPath = Split-Path -Path $script:PowerShellLambdaPath 54 | $script:CodeBuildRoot = Split-Path -Path $script:LambdaFunctionsPath 55 | } 56 | else { 57 | $script:CodeBuildRoot = $env:CODEBUILD_SRC_DIR 58 | } 59 | $script:ModulesRoot = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath 'Modules' 60 | $script:CFNParameterFilePath = Join-Path -Path $script:CodeBuildRoot -ChildPath 'CloudFormation' 61 | 62 | if (-not(Test-Path -Path $script:CFNParameterFilePath)) { 63 | throw ('Unable to find the cloudformation Path: {0}' -f $script:CFNParameterFilePath) 64 | } 65 | } 66 | 67 | # Synopsis: Imports PowerShell Modules 68 | task ImportModules { 69 | Write-Host 'Importing PowerShell Modules' 70 | 71 | $manifests = Get-ChildItem -Path $script:ModulesRoot -Recurse -Filter "*.psd1" 72 | foreach ($manifest in $manifests) { 73 | if (Get-Module -Name $manifest.BaseName) { 74 | Remove-Module -Name $manifest.BaseName 75 | } 76 | 77 | Write-Host ' -' $manifest.BaseName 78 | Import-Module $manifest.FullName -Force -Verbose:$false 79 | } 80 | } 81 | 82 | # Synopsis: Clean Artifacts Directory 83 | task Clean { 84 | foreach ($path in $script:ArtifactsPath, $script:StagingPath) { 85 | if (Test-Path -Path $path) { 86 | $null = Remove-Item -Path $path -Recurse -Force 87 | } 88 | 89 | $null = New-Item -ItemType Directory -Path $path -Force 90 | } 91 | } 92 | 93 | # Synopsis: Invokes Script Analyzer against the Module source path 94 | task Analyze { 95 | $scriptAnalyzerParams = @{ 96 | Path = $script:LambdaScriptPath 97 | Severity = @('Error', 'Warning') 98 | Recurse = $true 99 | ExcludeRule = @('PSAvoidUsingWriteHost', 'PSUseShouldProcessForStateChangingFunctions') 100 | Verbose = $false 101 | } 102 | 103 | $scriptAnalyzerResults = Invoke-ScriptAnalyzer @scriptAnalyzerParams 104 | 105 | if ($scriptAnalyzerResults) { 106 | $scriptAnalyzerResults | Format-Table 107 | throw 'One or more PSScriptAnalyzer errors/warnings where found.' 108 | } 109 | } 110 | 111 | # Synopsis: Builds the Module to the Artifacts folder 112 | task Build { 113 | Write-Verbose -Message 'Compiling the AWS Lambda Package' 114 | $script:LambdaPackage = New-AWSPowerShellLambdaPackage -ScriptPath $script:LambdaScriptPath -StagingDirectory $script:StagingPath -OutputPackage $script:ArtifactPackage -Verbose 115 | Write-Host 'AWS Lambda Function Handler:' $script:LambdaPackage.LambdaHandler 116 | Write-Host 'PowerShell Function Handler Environment Variable Name:' $script:LambdaPackage.PowerShellFunctionHandlerEnvVar 117 | } 118 | 119 | task Publish { 120 | if ([String]::IsNullOrWhiteSpace($env:CODEBUILD_SRC_DIR) -and [String]::IsNullOrWhiteSpace($env:CODEBUILD_RESOLVED_SOURCE_VERSION)) { 121 | Write-Warning -Message 'Not publishing the artifact because the code is not running inside a CodeBuild Project' 122 | } 123 | else { 124 | 'Publishing Lambda Function to S3' 125 | 126 | $s3Key = '{0}/{1}/lambdafunctions/PowerShell/{2}.zip' -f $env:S3_KEY_PREFIX, $env:CODEBUILD_RESOLVED_SOURCE_VERSION, $ScriptName 127 | ' - S3Key:/{0}/{1}' -f $env:ARTIFACT_S3_BUCKET, $s3Key 128 | 129 | Write-S3Object -BucketName $env:ARTIFACT_S3_BUCKET -Key $s3Key -File $script:ArtifactPackage 130 | 131 | # Update json parameter files 132 | $cfnLambdaS3KeyParameterName = 'LMFunctionS3Key{0}' -f $ScriptName 133 | Write-Verbose -Message "JSON Parameter Name for cloudformation S3 Key: $cfnLambdaS3KeyParameterName" -Verbose 134 | 135 | $cfnLambdaHandlerParameterName = 'LMFunctionHandler{0}' -f $ScriptName 136 | Write-Verbose -Message "JSON Parameter Name for Lambda Function Handler: $cfnLambdaHandlerParameterName" -Verbose 137 | 138 | $jsonFiles = Get-ChildItem -Path $script:CFNParameterFilePath -Recurse -Filter "*.json" 139 | foreach ($jsonFile in $jsonFiles) { 140 | 'Processing {0}' -f $jsonFile.Name 141 | $jsonData = Get-Content -Path $jsonFile.FullName -Raw | ConvertFrom-Json 142 | 143 | # Update cfnLambdaS3KeyParameterName in json parameters 144 | try { 145 | $jsonData.Parameters.$cfnLambdaS3KeyParameterName = $s3Key 146 | ' - Updated the json parameter "{0}" with "{1}".' -f $cfnLambdaS3KeyParameterName, $s3Key 147 | } 148 | catch { 149 | ' - Unable to find the json parameter "{0}". No modifications being made.' -f $cfnLambdaS3KeyParameterName 150 | } 151 | 152 | # Update cfnLambdaHandlerParameterName in json parameters 153 | try { 154 | $jsonData.Parameters.$cfnLambdaHandlerParameterName = $script:LambdaPackage.LambdaHandler 155 | ' - Updated the json parameter "{0}" with "{1}".' -f $cfnLambdaHandlerParameterName, $script:LambdaPackage.LambdaHandler 156 | } 157 | catch { 158 | ' - Unable to find the json parameter "{0}". No modifications being made.' -f $cfnLambdaHandlerParameterName 159 | } 160 | 161 | # Export the json back to disk 162 | $jsonData | ConvertTo-Json | Out-File -FilePath $jsonFile.FullName -Force -Encoding utf8 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /lambdafunctions/PowerShell/PubXMLMonitor/PubXMLMonitor.ps1: -------------------------------------------------------------------------------- 1 | # PowerShell script file to be executed as an AWS Lambda function. 2 | # 3 | # When executing in Lambda the following variables will be predefined. 4 | # $LambdaInput - A PSObject that contains the Lambda function input data. 5 | # $LambdaContext - An Amazon.Lambda.Core.ILambdaContext object that contains information about the currently running Lambda environment. 6 | # 7 | # The last item in the PowerShell pipeline will be returned as the result of the Lambda function. 8 | # 9 | # To include PowerShell modules with your Lambda function, like the AWS.Tools.Common module, add a "#Requires" statement 10 | # indicating the module and version. 11 | 12 | # Env variables that are set by the AWS Lambda environment: 13 | # $env:S3_BUCKET_NAME 14 | # $env:TELEGRAM_SECRET 15 | # $env:SERVICE_NAME 16 | 17 | #Requires -Modules @{ModuleName='AWS.Tools.Common';ModuleVersion='4.1.472'} 18 | #Requires -Modules @{ModuleName='AWS.Tools.CloudWatch';ModuleVersion='4.1.472'} 19 | #Requires -Modules @{ModuleName='AWS.Tools.SimpleSystemsManagement';ModuleVersion='4.1.472'} 20 | #Requires -Modules @{ModuleName='AWS.Tools.S3';ModuleVersion='4.1.472'} 21 | #Requires -Modules @{ModuleName='PoshGram';ModuleVersion='2.3.0'} 22 | 23 | # Uncomment to send the input event to CloudWatch Logs 24 | Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5) 25 | 26 | # CW Event Scheduled -> Lambda -> CW Metric 27 | 28 | #region supportingFunctions 29 | 30 | function Send-TelegramMessage { 31 | <# 32 | .SYNOPSIS 33 | Sends message to Telegram for notification. 34 | #> 35 | param ( 36 | [Parameter(Mandatory = $true, 37 | HelpMessage = 'Message to send to telegram')] 38 | [string] 39 | $Message 40 | ) 41 | 42 | if ($null -eq $script:telegramToken -or $null -eq $script:telegramChannel) { 43 | try { 44 | $getSSMParameterValueSplatToken = @{ 45 | Name = 'telegramtoken' 46 | WithDecryption = $true 47 | ErrorAction = 'Stop' 48 | } 49 | $getSSMParameterValueSplatChannel = @{ 50 | Name = 'telegramchannel' 51 | WithDecryption = $true 52 | ErrorAction = 'Stop' 53 | } 54 | $script:telegramToken = Get-SSMParameterValue @getSSMParameterValueSplatToken 55 | $script:telegramChannel = Get-SSMParameterValue @getSSMParameterValueSplatChannel 56 | } 57 | catch { 58 | throw $_ 59 | } 60 | } #if_token_channel_null 61 | 62 | if ([string]::IsNullOrWhiteSpace($script:telegramToken) -or [string]::IsNullOrWhiteSpace($script:telegramChannel)) { 63 | throw 'Parameters not successfully retrieved' 64 | } 65 | else { 66 | Write-Host 'Parameters retrieved.' 67 | $token = $script:telegramToken.Parameters.Value 68 | $channel = $script:telegramChannel.Parameters.Value 69 | $out = Send-TelegramTextMessage -BotToken $token -ChatID $channel -Message $Message 70 | Write-Host ($out | Out-String) 71 | } 72 | } #Send-TelegramMessage 73 | 74 | #endregion 75 | 76 | $key = '{0}.zip' -f $env:SERVICE_NAME 77 | Write-Host ('Retrieving {0} from {1}..' -f $key, $env:S3_BUCKET_NAME) 78 | try { 79 | $getS3ObjectSplat = @{ 80 | BucketName = $env:S3_BUCKET_NAME 81 | Key = $key 82 | ErrorAction = 'Stop' 83 | } 84 | $objInfo = Get-S3Object @getS3ObjectSplat 85 | } 86 | catch { 87 | Write-Warning -Message 'Error retrieving object from S3' 88 | Write-Error $_ 89 | Send-TelegramMessage -Message '\\\ Project PSGalleryExplorer - PubXMLMonitor Error retrieving object from S3' 90 | return 91 | } 92 | 93 | if ($null -eq $objInfo) { 94 | Write-Warning -Message 'No object returned from S3' 95 | Send-TelegramMessage -Message '\\\ Project PSGalleryExplorer - PubXMLMonitor The S3 object was not found' 96 | } 97 | 98 | Write-Host 'Getting current date' 99 | $now = Get-Date 100 | 101 | Write-Host 'Determining how old the object is' 102 | $diff = $now - $objInfo.LastModified 103 | $diffDays = $diff.Days 104 | Write-Host ('Object is {0} days old' -f $diffDays) 105 | 106 | # Create a MetricDatum .NET object 107 | $MetricDatum = [Amazon.CloudWatch.Model.MetricDatum]::new() 108 | $MetricDatum.MetricName = 'PubXMLAge' 109 | $MetricDatum.Value = $diffDays 110 | 111 | # Create a Dimension .NET object 112 | $Dimension = [Amazon.CloudWatch.Model.Dimension]::new() 113 | $Dimension.Name = 'PubXML' 114 | $Dimension.Value = 'DaysOld' 115 | 116 | # Assign the Dimension object to the MetricDatum's Dimensions property 117 | $MetricDatum.Dimensions = $Dimension 118 | 119 | $Namespace = 'PSGalleryExplorer' 120 | 121 | try { 122 | 123 | # Write the metric data to the CloudWatch service 124 | $writeCWMetricDataSplat = @{ 125 | Namespace = $Namespace 126 | MetricData = $MetricDatum 127 | ErrorAction = 'Stop' 128 | } 129 | Write-CWMetricData @writeCWMetricDataSplat 130 | } 131 | catch { 132 | $errorMessage = $_.Exception.Message 133 | Write-Error -Message ('Something went wrong: {0}' -f $errorMessage) 134 | Send-TelegramMessage -Message '\\\ Project PSGalleryExplorer - PubXMLMonitor Error sending metric data to CloudWatch' 135 | } 136 | 137 | return $true 138 | -------------------------------------------------------------------------------- /lambdafunctions/PowerShell/PubXMLMonitor/PubXMLMonitor.settings.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Use this for custom modifications to the build process if needed. 4 | #> 5 | 6 | ############################################################################### 7 | # Before/After Hooks for the Core Task: Clean 8 | ############################################################################### 9 | 10 | # Synopsis: Executes before the Clean task. 11 | #task BeforeClean -Before Clean {} 12 | 13 | # Synopsis: Executes after the Clean task. 14 | #task AfterClean -After Clean {} 15 | 16 | ############################################################################### 17 | # Before/After Hooks for the Core Task: Analyze 18 | ############################################################################### 19 | 20 | # Synopsis: Executes before the Analyze task. 21 | #task BeforeAnalyze -Before Analyze {} 22 | 23 | # Synopsis: Executes after the Analyze task. 24 | #task AfterAnalyze -After Analyze {} 25 | 26 | ############################################################################### 27 | # Before/After Hooks for the Core Task: Archive 28 | ############################################################################### 29 | 30 | # Synopsis: Executes before the Archive task. 31 | #task BeforeArchive -Before Archive {} 32 | 33 | # Synopsis: Executes after the Archive task. 34 | #task AfterArchive -After Archive {} 35 | 36 | ############################################################################### 37 | # Before/After Hooks for the Core Task: Build 38 | ############################################################################### 39 | 40 | # Synopsis: Executes before the Build task. 41 | #task BeforeBuild -Before Build {} 42 | 43 | # Synopsis: Executes after the Build task. 44 | #task AfterBuild -After Build {} 45 | 46 | ############################################################################### 47 | # Before/After Hooks for the Core Task: Test 48 | ############################################################################### 49 | 50 | # Synopsis: Executes before the Test Task. 51 | #task BeforeTest -Before Test {} 52 | 53 | # Synopsis: Executes after the Test Task. 54 | #task AfterTest -After Test {} 55 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | # https://www.mkdocs.org/user-guide/configuration/ 2 | # https://www.mkdocs.org/user-guide/writing-your-docs/ 3 | # https://www.mkdocs.org/user-guide/writing-your-docs/#writing-with-markdown 4 | # https://mkdocs.readthedocs.io/en/0.15.2/user-guide/writing-your-docs/ 5 | # https://mkdocs.readthedocs.io/en/0.15.2/user-guide/styling-your-docs/ 6 | # https://example-mkdocs-basic.readthedocs.io/en/latest/ 7 | # https://github.com/mkdocs/mkdocs/blob/master/mkdocs.yml 8 | # https://squidfunk.github.io/mkdocs-material/creating-your-site/ 9 | # mkdocs.yml 10 | 11 | site_name: PSGalleryExplorer - search, explore, and discover PowerShell Gallery modules 12 | # site_url: 13 | repo_url: https://github.com/techthoughts2/PSGalleryExplorer 14 | # repo_name: 15 | # edit_uri: edit/main/docs/ 16 | # edit_uri_template: 17 | site_description: PSGalleryExplorer is a PowerShell module that lets you search, explore, and discover PowerShell Gallery modules based on additional criteria. # meta tag to the generated HTML header 18 | site_author: Jake Morrison # meta tag to the generated HTML header 19 | copyright: "PSGalleryExplorer is licensed under the MIT license" 20 | # remote_branch: 21 | # remote_name: 22 | # docs_dir: docs 23 | # site_dir: 24 | # extra_css: 25 | # extra_javascript: 26 | markdown_extensions: 27 | # Python Markdown 28 | - admonition 29 | - toc: 30 | permalink: true 31 | # code highlighting 32 | - pymdownx.highlight: 33 | use_pygments: true 34 | - pymdownx.highlight: 35 | anchor_linenums: true 36 | - pymdownx.inlinehilite 37 | - pymdownx.snippets 38 | - pymdownx.superfences 39 | 40 | # extra_templates: 41 | # extra: 42 | theme: 43 | name: material 44 | # language: en 45 | # custom_dir: overrides 46 | features: 47 | # - navigation.tabs 48 | # - navigation.tabs.sticky 49 | # - navigation.path 50 | favicon: assets/PSGalleryExplorer_favicon-32x32.png 51 | icon: 52 | repo: fontawesome/brands/github 53 | # font: 54 | # text: Work Sans 55 | logo: assets/PSGalleryExplorerIcon.png 56 | # palette: 57 | # primary: teal 58 | palette: 59 | # Palette toggle for light mode 60 | - media: "(prefers-color-scheme: light)" 61 | scheme: default 62 | primary: light blue 63 | accent: deep purple 64 | toggle: 65 | icon: material/brightness-7 66 | name: Switch to dark mode 67 | 68 | # Palette toggle for dark mode 69 | - media: "(prefers-color-scheme: dark)" 70 | scheme: slate 71 | primary: indigo 72 | accent: light blue 73 | toggle: 74 | icon: material/brightness-4 75 | name: Switch to light mode 76 | nav: 77 | - Overview: index.md 78 | - Functions: 79 | - Find-ModuleByCommand.md: Find-ModuleByCommand.md 80 | - Find-PSGModule.md: Find-PSGModule.md 81 | - Data Collection: PSGalleryExplorer-Data_Collection.md 82 | - Metrics: PSGalleryExplorer-Metrics.md 83 | - FAQ: PSGalleryExplorer-FAQ.md 84 | - Change Log: CHANGELOG.md 85 | # - Functions: 86 | # - Function1: functions/function1.md 87 | 88 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer.Settings.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techthoughts2/PSGalleryExplorer/adbf98fe62f59219f97aeeb2c9271b556cf41535/src/PSGalleryExplorer.Settings.ps1 -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Imports.ps1: -------------------------------------------------------------------------------- 1 | # This is a locally sourced Imports file for local development. 2 | # It can be imported by the psm1 in local development to add script level variables. 3 | # It will merged in the build process. This is for local development only. 4 | 5 | #region script variables 6 | 7 | function Get-DataLocation { 8 | $folderName = "PSGalleryExplorer" 9 | if ($PROFILE) { 10 | $script:dataPath = (Join-Path (Split-Path -Parent $PROFILE) $folderName) 11 | } 12 | else { 13 | $script:dataPath = "~\${$folderName}" 14 | } 15 | } 16 | 17 | $script:corps = @( 18 | '2AT B.V.' 19 | '3Shape A/S' 20 | 'AWS' 21 | 'Amazon' 22 | 'Amazon.com, Inc' 23 | 'Amazon Web Services' 24 | 'AtlassianPS' 25 | 'BAMCIS' 26 | 'Bentley Systems, Incorporated' 27 | 'BitTitan' 28 | 'BitTitan, Inc.' 29 | '(c) 2014 Microsoft Corporation. All rights reserved.' 30 | 'CData Software, Inc.' 31 | 'Chocolatey Software' 32 | 'Cisco Systems' 33 | 'Cisco' 34 | 'Cisco Systems, Inc.' 35 | 'Cybersecurity Engineering' 36 | 'DSC Community' 37 | 'Dell Inc.' 38 | 'Dell Technologies' 39 | 'Dell' 40 | 'DELL|EMC' 41 | 'DELL||EMC' 42 | 'Dell EMC' 43 | 'DevScope' 44 | 'Docker Inc.' 45 | 'Docker' 46 | 'Evotec' 47 | 'Google' 48 | 'Google Inc' 49 | 'Google Inc.' 50 | 'Hewlett Packard Enterprise Co.' 51 | 'Hewlett Packard Enterprise' 52 | 'Hewlett-Packard Enterprise' 53 | 'Hewlett Packard Enterprise Development LP' 54 | 'HP Development Company L.P.' 55 | 'HP Inc' 56 | 'HPE Storage, A Hewlett Packard Enterprise Company' 57 | 'https://github.com/ebekker/ACMESharp' 58 | 'Ironman Software, LLC' 59 | 'Ironman Software' 60 | 'JDH Information Technology Solutions, Inc.' 61 | 'JumpCloud' 62 | 'Kelverion' 63 | 'Kelverion Automation Limited' 64 | 'Lockstep Technology Group' 65 | 'Microsoft 365 Patterns and Practices' 66 | 'Microsoft (Xbox)' 67 | 'Microsoft Corp' 68 | 'Microsoft Inc.' 69 | 'Microsoft Corporation' 70 | 'Microsoft Corportation' 71 | 'Microsoft Corpration' 72 | 'Microsoft' 73 | 'Microsoft | Services' 74 | 'Microsoft CSS' 75 | 'MicrosoftCorporation' 76 | 'Microsoft Corp.' 77 | 'Microsoft Germany GmbH' 78 | 'Microsoft Support' 79 | 'MosaicMK Software LLC' 80 | 'MosaicMKSoftwareLLC' 81 | 'Mozilla Corporation' 82 | 'Nimble Storage, A Hewlett Packard Enterprise Company' 83 | 'Noveris Pty Ltd' 84 | 'Octopus Deploy Pty. Ltd' 85 | 'Octopus Deploy' 86 | 'Oracle Cloud Infrastructure' 87 | 'Oracle Corporation' 88 | 'Pentia A/S' 89 | 'Pentia' 90 | 'PowerShell.org' 91 | 'Pure Storage, Inc.' 92 | 'Red Gate Software Ltd.' 93 | 'SecureMFA' 94 | 'SecureMFA.com' 95 | 'SolarNet' 96 | 'SolarWinds Worldwide, LLC.' 97 | 'SolarWinds' 98 | 'SynEdgy Limited' 99 | 'Synergex International Corporation' 100 | 'Transitional Data Services, Inc.' 101 | 'VMware' 102 | 'VMware, Inc.' 103 | 'VMware Inc.' 104 | 'Virtual Engine' 105 | 'waldo.be' 106 | 'WebMD Health Services' 107 | 'Worxspace' 108 | 'XtremIO Dell EMC' 109 | 'Yevrag35, LLC.' 110 | 'Zerto Ltd.' 111 | ) 112 | $script:regulars = @( 113 | 'BuildHelpers' 114 | 'BurntToast' 115 | 'Carbon' 116 | 'ChocolateyGet' 117 | 'CredentialManager' 118 | 'dbatools' 119 | 'Foil' 120 | 'ImportExcel' 121 | 'Invokebuild' 122 | 'Invoke-CommandAs' 123 | 'oh-my-posh' 124 | 'PendingReboot' 125 | 'PSDepend' 126 | 'PSDeploy' 127 | 'PSKoans' 128 | 'PSLogging' 129 | 'PSSlack' 130 | 'PSWindowsUpdate' 131 | 'Pester' 132 | 'PoshBot' 133 | 'posh-git' 134 | 'Posh-SSH' 135 | 'powershell-yaml' 136 | 'psake' 137 | 'RunAsUser' 138 | 'Selenium' 139 | 'SnipeitPS' 140 | 'SNMP' 141 | 'TeamViewerPS' 142 | 'Write-ObjectToSQL' 143 | ) 144 | 145 | $domain = 'cloudfront.net' 146 | $target = 'dfuu1myynofuh' 147 | Get-DataLocation 148 | $script:dataFileZip = 'PSGalleryExplorer.zip' 149 | $script:metadataFile = 'PSGalleryExplorer.json' 150 | $script:dataFile = 'PSGalleryExplorer.xml' 151 | $script:dlURI = '{0}.{1}' -f $target, $domain 152 | $script:glData = $null 153 | 154 | #endregion 155 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/PSGalleryExplorer.Format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PSGETable 6 | 7 | PSGEFormat 8 | 9 | 10 | 11 | 12 | 20 13 | 14 | 15 | 9 16 | right 17 | 18 | 19 | 7 20 | right 21 | 22 | 23 | 7 24 | right 25 | 26 | 27 | 7 28 | right 29 | 30 | 31 | 6 32 | right 33 | 34 | 35 | left 36 | 37 | 38 | 39 | 40 | 41 | 42 | Name 43 | 44 | 45 | Downloads 46 | 47 | 48 | Star 49 | 50 | 51 | Fork 52 | 53 | 54 | Issues 55 | 56 | 57 | Sub 58 | 59 | 60 | Description 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | PSGEList 69 | 70 | PSGEFormat 71 | 72 | 73 | 74 | 75 | 76 | 77 | Name 78 | 79 | 80 | Downloads 81 | 82 | 83 | LastUpdate 84 | 85 | 86 | Star 87 | 88 | 89 | Issues 90 | 91 | 92 | Sub 93 | 94 | 95 | Watch 96 | 97 | 98 | Fork 99 | 100 | 101 | RepoUpdate 102 | 103 | 104 | Description 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | PSGEInsightTable 114 | 115 | PSGEInsight 116 | 117 | 118 | 119 | 120 | 20 121 | 122 | 123 | 9 124 | right 125 | 126 | 127 | 7 128 | right 129 | 130 | 131 | 7 132 | right 133 | 134 | 135 | 6 136 | right 137 | 138 | 139 | 10 140 | right 141 | 142 | 143 | 6 144 | right 145 | 146 | 147 | left 148 | 149 | 150 | 151 | 152 | 153 | 154 | Name 155 | 156 | 157 | Downloads 158 | 159 | 160 | Star 161 | 162 | 163 | Fork 164 | 165 | 166 | Watch 167 | 168 | 169 | ModuleSize 170 | 171 | 172 | ModuleFileCount 173 | 174 | 175 | RepoUpdate 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | PSGEInsightList 184 | 185 | PSGEInsight 186 | 187 | 188 | 189 | 190 | 191 | 192 | Name 193 | 194 | 195 | Downloads 196 | 197 | 198 | Star 199 | 200 | 201 | Fork 202 | 203 | 204 | Watch 205 | 206 | 207 | ModuleSize 208 | 209 | 210 | ModuleFileCount 211 | 212 | 213 | RepoUpdate 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/PSGalleryExplorer.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PSGalleryExplorer' 3 | # 4 | # Generated by: Jake Morrison 5 | # 6 | # Generated on: 12/21/19 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PSGalleryExplorer.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '2.5.4' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = 'e9252e8e-2073-4084-9562-cf60ad84603d' 22 | 23 | # Author of this module 24 | Author = 'Jake Morrison' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'TechThoughts' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) Jake Morrison. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'Search, explore, and discover PowerShell Gallery modules based on various criteria.' 34 | 35 | # Minimum version of the PowerShell engine required by this module 36 | PowerShellVersion = '5.1' 37 | 38 | # Name of the PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # CLRVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | RequiredModules = @( 55 | @{ 56 | ModuleName = 'Convert' 57 | ModuleVersion = '1.5.0' 58 | } 59 | ) 60 | 61 | # Assemblies that must be loaded prior to importing this module 62 | # RequiredAssemblies = @() 63 | 64 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 65 | # ScriptsToProcess = @() 66 | 67 | # Type files (.ps1xml) to be loaded when importing this module 68 | # TypesToProcess = @() 69 | 70 | # Format files (.ps1xml) to be loaded when importing this module 71 | FormatsToProcess = @( 72 | 'PSGalleryExplorer.Format.ps1xml' 73 | ) 74 | 75 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 76 | # NestedModules = @() 77 | 78 | # 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. 79 | FunctionsToExport = @( 80 | 'Find-ModuleByCommand' 81 | 'Find-PSGModule' 82 | ) 83 | 84 | # 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. 85 | # CmdletsToExport = '*' 86 | 87 | # Variables to export from this module 88 | # VariablesToExport = '*' 89 | 90 | # 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. 91 | # AliasesToExport = '*' 92 | 93 | # DSC resources to export from this module 94 | # DscResourcesToExport = @() 95 | 96 | # List of all modules packaged with this module 97 | # ModuleList = @() 98 | 99 | # List of all files packaged with this module 100 | # FileList = @() 101 | 102 | # 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. 103 | PrivateData = @{ 104 | 105 | PSData = @{ 106 | 107 | # Tags applied to this module. These help with module discovery in online galleries. 108 | Tags = @( 109 | 'discover', 110 | 'find', 111 | 'gallery', 112 | 'github', 113 | 'gitlab', 114 | 'bitbucket', 115 | 'module', 116 | 'modules', 117 | 'powershell', 118 | 'powershellmodule', 119 | 'psgallery', 120 | 'search' 121 | ) 122 | 123 | # A URL to the license for this module. 124 | LicenseUri = 'https://github.com/techthoughts2/PSGalleryExplorer/blob/main/LICENSE' 125 | 126 | # A URL to the main website for this project. 127 | ProjectUri = 'https://github.com/techthoughts2/PSGalleryExplorer' 128 | 129 | # A URL to an icon representing this module. 130 | IconUri = 'https://github.com/techthoughts2/PSGalleryExplorer/raw/main/docs/assets/PSGalleryExplorerIcon.png' 131 | 132 | # ReleaseNotes of this module 133 | ReleaseNotes = 'https://github.com/techthoughts2/PSGalleryExplorer/blob/main/docs/CHANGELOG.md' 134 | 135 | # Prerelease string of this module 136 | # Prerelease = '' 137 | 138 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 139 | RequireLicenseAcceptance = $false 140 | 141 | # External dependent modules of this module 142 | # ExternalModuleDependencies = @() 143 | 144 | } # End of PSData hashtable 145 | 146 | } # End of PrivateData hashtable 147 | 148 | # HelpInfo URI of this module 149 | # HelpInfoURI = '' 150 | 151 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 152 | # DefaultCommandPrefix = '' 153 | 154 | } 155 | 156 | 157 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/PSGalleryExplorer.psm1: -------------------------------------------------------------------------------- 1 | # this psm1 is for local testing and development use only 2 | 3 | # dot source the parent import for local development variables 4 | . $PSScriptRoot\Imports.ps1 5 | 6 | # discover all ps1 file(s) in Public and Private paths 7 | 8 | $itemSplat = @{ 9 | Filter = '*.ps1' 10 | Recurse = $true 11 | ErrorAction = 'Stop' 12 | } 13 | try { 14 | $public = @(Get-ChildItem -Path "$PSScriptRoot\Public" @itemSplat) 15 | $private = @(Get-ChildItem -Path "$PSScriptRoot\Private" @itemSplat) 16 | } 17 | catch { 18 | Write-Error $_ 19 | throw "Unable to get get file information from Public & Private src." 20 | } 21 | 22 | # dot source all .ps1 file(s) found 23 | foreach ($file in @($public + $private)) { 24 | try { 25 | . $file.FullName 26 | } 27 | catch { 28 | throw "Unable to dot source [$($file.FullName)]" 29 | 30 | } 31 | } 32 | 33 | # export all public functions 34 | Export-ModuleMember -Function $public.Basename -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Private/Confirm-DataLocation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Confirm data output location. Creates output dir if not present. 4 | .DESCRIPTION 5 | Evaluates presence of data output location for xml dataset. If the directory is not found, it will be created. 6 | .EXAMPLE 7 | Confirm-DataLocation 8 | 9 | Confirms presence of data output location. Creates if not found. 10 | .OUTPUTS 11 | System.Boolean 12 | .NOTES 13 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 14 | .COMPONENT 15 | PSGalleryExplorer 16 | #> 17 | function Confirm-DataLocation { 18 | [CmdletBinding()] 19 | param ( 20 | ) 21 | $result = $true #assume the best 22 | Write-Verbose -Message 'Verifying data set output location...' 23 | try { 24 | $pathEval = Test-Path -Path $script:dataPath -ErrorAction Stop 25 | } 26 | catch { 27 | $result = $false 28 | Write-Error $_ 29 | return $result 30 | } 31 | 32 | if (-not ($pathEval)) { 33 | Write-Verbose -Message 'Creating output directory...' 34 | try { 35 | $newItemSplat = @{ 36 | ItemType = 'Directory' 37 | Path = $script:dataPath 38 | ErrorAction = 'Stop' 39 | } 40 | $null = New-Item @newItemSplat 41 | Write-Verbose -Message 'Created.' 42 | } 43 | catch { 44 | $result = $false 45 | Write-Error $_ 46 | return $result 47 | } 48 | } #if_TestPath 49 | else { 50 | Write-Verbose 'Data path confirmed.' 51 | } #else_TestPath 52 | 53 | return $result 54 | } #Confirm-DataLocation 55 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Private/Confirm-MetadataUpdate.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Compares the local metadata file to the remote metadata file. 4 | .DESCRIPTION 5 | Evaluates the local metadata file and compares it to the remote metadata file. If the files are the same, returns true. If the files are different, returns false. 6 | .EXAMPLE 7 | Confirm-MetadataUpdate 8 | 9 | Compares the local metadata file to the remote metadata file. 10 | .OUTPUTS 11 | System.Boolean 12 | .NOTES 13 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 14 | .COMPONENT 15 | PSGalleryExplorer 16 | #> 17 | function Confirm-MetadataUpdate { 18 | [CmdletBinding()] 19 | param ( 20 | ) 21 | $result = $true #assume the best 22 | 23 | Write-Verbose -Message 'Checking for metadata file...' 24 | $localMetaDataFilePath = [System.IO.Path]::Combine($script:dataPath, $script:metadataFile) 25 | try { 26 | $pathEval = Test-Path -Path $localMetaDataFilePath -ErrorAction Stop 27 | } 28 | catch { 29 | $result = $false 30 | Write-Error $_ 31 | return $result 32 | } 33 | 34 | if (-not ($pathEval)) { 35 | $result = $false 36 | } #if_pathEval 37 | else { 38 | Write-Verbose 'Metadata file found. Performing metadata comparison...' 39 | try { 40 | $localMetadata = Get-Content $localMetaDataFilePath -ErrorAction 'Stop' | ConvertFrom-Json 41 | } 42 | catch { 43 | $result = $false 44 | Write-Error $_ 45 | return $result 46 | } 47 | 48 | $tempMetadataFile = '{0}_temp' -f $script:metadataFile 49 | $tempMetadataFilePath = [System.IO.Path]::Combine($script:dataPath, $tempMetadataFile) 50 | # if the temp metadata file exists, delete it 51 | if (Test-Path -Path $tempMetadataFile) { 52 | Remove-Item -Path $tempMetadataFilePath -Force 53 | } 54 | 55 | # download metadata file for comparison 56 | $fileFetchStatus = Get-RemoteFile -File $script:metadataFile -OutFile $tempMetadataFile 57 | if ($fileFetchStatus -eq $false) { 58 | Write-Error 'Unable to download metadata file.' 59 | $result = $false 60 | return $result 61 | } 62 | 63 | try { 64 | $remoteMetadata = Get-Content $tempMetadataFilePath -ErrorAction 'Stop' | ConvertFrom-Json 65 | } 66 | catch { 67 | $result = $false 68 | Write-Error $_ 69 | return $result 70 | } 71 | 72 | Write-Verbose -Message ('{0} vs {1}' -f $localMetadata.zipCreated, $remoteMetadata.zipCreated) 73 | if ($localMetadata.zipCreated -eq $remoteMetadata.zipCreated) { 74 | Write-Verbose 'Metadata file is current.' 75 | } 76 | else { 77 | Write-Verbose 'Metadata file requires refresh.' 78 | $result = $false 79 | } 80 | } 81 | 82 | return $result 83 | } #Confirm-MetadataUpdate 84 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Private/Confirm-XMLDataSet.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Confirms the XML dataset file is available and not beyond the expiration time. 4 | .DESCRIPTION 5 | Determines if the XML dataset file is stale or not available. 6 | If the file is not available, false will be returned so it can be downloaded. 7 | If the file is available, but over 9 days old, the metadata file will be checked to see if an update is available. 8 | If an update is available after the metadata file is checked, false will be returned so the data file can be refreshed. 9 | .EXAMPLE 10 | Confirm-XMLDataSet 11 | 12 | Checks for XML dataset and determines if it is 9 days older or more. 13 | .OUTPUTS 14 | System.Boolean 15 | .NOTES 16 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 17 | .COMPONENT 18 | PSGalleryExplorer 19 | #> 20 | function Confirm-XMLDataSet { 21 | [CmdletBinding()] 22 | param ( 23 | ) 24 | $result = $true #assume the best 25 | $dataFile = '{0}/{1}' -f $script:dataPath, $script:dataFile 26 | 27 | Write-Verbose -Message 'Confirming valid and current data set...' 28 | 29 | # if the file doesn't exist, we need to download it 30 | Write-Verbose -Message 'Checking for data file...' 31 | try { 32 | $pathEval = Test-Path -Path $dataFile -ErrorAction Stop 33 | } 34 | catch { 35 | $result = $false 36 | Write-Error $_ 37 | return $result 38 | } 39 | 40 | if (-not ($pathEval)) { 41 | $result = $false 42 | } #if_pathEval 43 | else { 44 | Write-Verbose 'Data file found. Checking date of file...' 45 | try { 46 | $fileData = Get-Item -Path $dataFile -ErrorAction Stop 47 | } 48 | catch { 49 | $result = $false 50 | Write-Error $_ 51 | return $result 52 | } 53 | if ($fileData) { 54 | $creationDate = $fileData.LastWriteTime 55 | $now = Get-Date 56 | if (($now - $creationDate).Days -ge 9) { 57 | # Write-Verbose 'Data file requires refresh.' 58 | Write-Verbose 'Data file is older than 9 days. Checking if an update is available...' 59 | $metadataStatus = Confirm-MetadataUpdate 60 | if ($metadataStatus -eq $false) { 61 | Write-Verbose 'Refreshing data file...' 62 | $result = $false 63 | } 64 | else { 65 | Write-Verbose 'No update available. Data file is current.' 66 | } 67 | } 68 | else { 69 | Write-Verbose 'Data file verified' 70 | } 71 | } #if_fileData 72 | else { 73 | Write-Warning 'Unable to retrieve file information for PSGalleryExplorer data set.' 74 | $result = $false 75 | return $result 76 | } #else_fileData 77 | } #else_pathEval 78 | 79 | return $result 80 | } #Confirm-XMLDataSet 81 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Private/Expand-XMLDataSet.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Unzips the XML data set. 4 | .DESCRIPTION 5 | Evaluates for previous version of XML data set and removes if required. Expands the XML data set for use. 6 | .EXAMPLE 7 | Expand-XMLDataSet 8 | 9 | Unzips and expands the XML data set. 10 | .OUTPUTS 11 | System.Boolean 12 | .NOTES 13 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 14 | .COMPONENT 15 | PSGalleryExplorer 16 | #> 17 | function Expand-XMLDataSet { 18 | [CmdletBinding()] 19 | param ( 20 | ) 21 | $result = $true #assume the best 22 | $dataFile = '{0}/{1}' -f $script:dataPath, $script:dataFile 23 | 24 | Write-Verbose -Message 'Testing if data set file already exists...' 25 | try { 26 | $pathEval = Test-Path -Path $dataFile -ErrorAction Stop 27 | Write-Verbose -Message "EVAL: $true" 28 | } 29 | catch { 30 | $result = $false 31 | Write-Error $_ 32 | return $result 33 | } 34 | 35 | if ($pathEval) { 36 | Write-Verbose -Message 'Removing existing data set file...' 37 | try { 38 | $removeItemSplat = @{ 39 | Force = $true 40 | Path = $dataFile 41 | ErrorAction = 'Stop' 42 | } 43 | Remove-Item @removeItemSplat 44 | } #try 45 | catch { 46 | $result = $false 47 | Write-Error $_ 48 | return $result 49 | } #catch 50 | } #if_pathEval 51 | 52 | Write-Verbose -Message 'Expanding data set archive...' 53 | try { 54 | $expandArchiveSplat = @{ 55 | DestinationPath = $script:dataPath 56 | Force = $true 57 | ErrorAction = 'Stop' 58 | Path = '{0}/{1}' -f $script:dataPath, $script:dataFileZip 59 | } 60 | $null = Expand-Archive @expandArchiveSplat 61 | Write-Verbose -Message 'Expand completed.' 62 | } #try 63 | catch { 64 | $result = $false 65 | Write-Error $_ 66 | } #catch 67 | 68 | return $result 69 | } #Expand-XMLDataSet 70 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Private/Get-RemoteFile.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads file to device. 4 | .DESCRIPTION 5 | Retrieves file from web and downloads to device. 6 | .EXAMPLE 7 | Get-RemoteFile 8 | 9 | Downloads file to data path. 10 | .PARAMETER File 11 | File to download. 12 | .PARAMETER OutFileName 13 | Specify output file name. 14 | .OUTPUTS 15 | System.Boolean 16 | .NOTES 17 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 18 | Overwrites existing zip file. 19 | .COMPONENT 20 | PSGalleryExplorer 21 | #> 22 | function Get-RemoteFile { 23 | [CmdletBinding()] 24 | param ( 25 | [Parameter(Mandatory = $true, 26 | HelpMessage = 'File to download')] 27 | [string]$File, 28 | 29 | [Parameter(Mandatory = $false, 30 | HelpMessage = 'Specify output file name.')] 31 | [string]$OutFileName 32 | ) 33 | $result = $true #assume the best 34 | 35 | if ($OutFileName) { 36 | $OutFile = $OutFileName 37 | } 38 | else { 39 | $OutFile = $File 40 | } 41 | 42 | Write-Verbose -Message 'Downloading file...' 43 | try { 44 | $invokeWebRequestSplat = @{ 45 | OutFile = [System.IO.Path]::Combine($script:dataPath, $OutFile) 46 | Uri = 'https://{0}/{1}' -f $script:dlURI, $File 47 | ErrorAction = 'Stop' 48 | } 49 | $oldProgressPreference = $progressPreference 50 | $progressPreference = 'SilentlyContinue' 51 | if ($PSEdition -eq 'Desktop') { 52 | $null = Invoke-WebRequest @invokeWebRequestSplat -PassThru -UseBasicParsing 53 | } 54 | else { 55 | $null = Invoke-WebRequest @invokeWebRequestSplat -PassThru 56 | } 57 | } #try 58 | catch { 59 | $result = $false 60 | Write-Error $_ 61 | } #catch 62 | finally { 63 | $progressPreference = $oldProgressPreference 64 | } #finally 65 | return $result 66 | } #Get-RemoteFile 67 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Private/Import-XMLDataSet.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Evaluates if XML data set is in memory and kicks of child processes to obtain XML data set. 4 | .DESCRIPTION 5 | XML data set will be evaluated if already in memory. If not, a series of processes will be kicked off to load the XML data set for use. 6 | .EXAMPLE 7 | Import-XMLDataSet 8 | 9 | Loads the XML data set into memory. 10 | .OUTPUTS 11 | System.Boolean 12 | .NOTES 13 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 14 | Parent process for getting XML data. 15 | .COMPONENT 16 | PSGalleryExplorer 17 | #> 18 | function Import-XMLDataSet { 19 | [CmdletBinding()] 20 | param ( 21 | ) 22 | $result = $true #assume the best 23 | Write-Verbose -Message 'Verifying current state of XML data set...' 24 | if ($null -eq $script:glData) { 25 | $dataCheck = Invoke-XMLDataCheck 26 | if ($dataCheck) { 27 | try { 28 | $getContentSplat = @{ 29 | Path = "$script:dataPath\$script:dataFile" 30 | Raw = $true 31 | ErrorAction = 'Stop' 32 | } 33 | $fileData = Get-Content @getContentSplat 34 | $script:glData = $fileData | ConvertFrom-Clixml -ErrorAction Stop 35 | } #try 36 | catch { 37 | $result = $false 38 | Write-Error $_ 39 | } #catch 40 | } #if_dataCheck 41 | else { 42 | $result = $false 43 | } #else_dataCheck 44 | } #if_gldata 45 | return $result 46 | } #Import-XMLDataSet 47 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Private/Invoke-XMLDataCheck.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Invokes all child functions required to process retrieving the XML data set file. 4 | .DESCRIPTION 5 | Runs all required child functions to successfully retrieve and process the XML data set file. 6 | .EXAMPLE 7 | Invoke-XMLDataCheck 8 | 9 | Downloads, expands, and verified the XML data set file. 10 | .OUTPUTS 11 | System.Boolean 12 | .NOTES 13 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 14 | Confirm-XMLDataSet 15 | Get-RemoteFile 16 | Expand-XMLDataSet 17 | .COMPONENT 18 | PSGalleryExplorer 19 | #> 20 | function Invoke-XMLDataCheck { 21 | [CmdletBinding(ConfirmImpact = 'Low', 22 | SupportsShouldProcess = $true)] 23 | param ( 24 | [Parameter(Mandatory = $false, 25 | HelpMessage = 'Skip confirmation')] 26 | [switch]$Force 27 | ) 28 | Begin { 29 | 30 | if (-not $PSBoundParameters.ContainsKey('Verbose')) { 31 | $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') 32 | } 33 | if (-not $PSBoundParameters.ContainsKey('Confirm')) { 34 | $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference') 35 | } 36 | if (-not $PSBoundParameters.ContainsKey('WhatIf')) { 37 | $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference') 38 | } 39 | 40 | Write-Verbose -Message ('[{0}] Confirm={1} ConfirmPreference={2} WhatIf={3} WhatIfPreference={4}' -f $MyInvocation.MyCommand, $Confirm, $ConfirmPreference, $WhatIf, $WhatIfPreference) 41 | 42 | $results = $true #assume the best 43 | } #begin 44 | Process { 45 | # -Confirm --> $ConfirmPreference = 'Low' 46 | # ShouldProcess intercepts WhatIf* --> no need to pass it on 47 | if ($Force -or $PSCmdlet.ShouldProcess("ShouldProcess?")) { 48 | Write-Verbose -Message ('[{0}] Reached command' -f $MyInvocation.MyCommand) 49 | $ConfirmPreference = 'None' 50 | 51 | $dataOutputDir = Confirm-DataLocation 52 | if ($dataOutputDir -eq $true) { 53 | $confirm = Confirm-XMLDataSet 54 | if (-not $confirm -eq $true) { 55 | $retrieve = Get-RemoteFile -File $script:dataFileZip 56 | # remove metadata file if it exists 57 | $localMetaDataFilePath = [System.IO.Path]::Combine($script:dataPath, $script:metadataFile) 58 | if (Test-Path -Path $localMetaDataFilePath) { 59 | Remove-Item -Path $localMetaDataFilePath -Force 60 | } 61 | $retrieveMetadata = Get-RemoteFile -File $script:metadataFile 62 | if ($retrieve -eq $true -and $retrieveMetadata -eq $true) { 63 | $expand = Expand-XMLDataSet 64 | if (-not $expand -eq $true) { 65 | $results = $false 66 | } 67 | } 68 | else { 69 | $results = $false 70 | } 71 | } #if_Confirm 72 | } #if_data_output 73 | else { 74 | $results = $false 75 | } #else_data_output 76 | 77 | } #if_Should 78 | } #process 79 | End { 80 | return $results 81 | } #end 82 | } #Invoke-XMLDataCheck 83 | -------------------------------------------------------------------------------- /src/PSGalleryExplorer/Public/Find-ModuleByCommand.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Searches for modules that contain a specific command or cmdlet name. 4 | .DESCRIPTION 5 | The Find-ModuleByCommand cmdlet searches for modules on the PowerShell Gallery that contain a specified command or cmdlet name. The cmdlet returns a list of modules that include the command or cmdlet, along with key metrics and information about the module. This cmdlet is useful when you need to quickly find a module that includes a particular command or cmdlet, without having to install or download the module first. 6 | .EXAMPLE 7 | Find-ModuleByCommand -CommandName New-ModuleProject 8 | 9 | Returns a list of modules that contain the command New-ModuleProject 10 | .EXAMPLE 11 | Find-ModuleByCommand -CommandName 'Send-TelegramTextMessage' 12 | 13 | Returns a list of modules that contain the command Send-TelegramTextMessage 14 | .EXAMPLE 15 | Find-ModuleByCommand -CommandName 'Send-TelegramTextMessage' -InsightView 16 | 17 | Returns a list of modules that contain the command Send-TelegramTextMessage, with focused community insights about the module 18 | .PARAMETER CommandName 19 | Specifies the command name to search for 20 | .PARAMETER InsightView 21 | Output focuses on additional insights available through PSGalleryExplorer. This includes the module's size and file count, as well as repository metrics like stars, forks, and last repo update date 22 | .OUTPUTS 23 | PSGEFormat 24 | .NOTES 25 | Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ 26 | .COMPONENT 27 | PSGalleryExplorer 28 | .LINK 29 | https://psgalleryexplorer.readthedocs.io/en/latest/Find-ModuleByCommand/ 30 | #> 31 | function Find-ModuleByCommand { 32 | [CmdletBinding()] 33 | param ( 34 | [Parameter(Mandatory = $true, 35 | HelpMessage = 'Specifies the command name to search for')] 36 | [string]$CommandName, 37 | 38 | [Parameter(Mandatory = $false, 39 | HelpMessage = 'Output focus on community insights')] 40 | [switch]$InsightView 41 | ) 42 | Write-Verbose -Message 'Verifying XML Data Set Availability...' 43 | if (Import-XMLDataSet) { 44 | Write-Verbose -Message 'Verified.' 45 | $dataSet = $script:glData 46 | 47 | Write-Verbose -Message ('Searching for Modules that contain the command: {0}' -f $CommandName) 48 | $find = $dataSet | Where-Object { 49 | $_.Includes.Function -contains $CommandName -or 50 | $_.Includes.Command -contains $CommandName -or 51 | $_.Includes.Cmdlet -contains $CommandName 52 | } 53 | 54 | } #if_Import-XMLDataSet 55 | else { 56 | Write-Warning -Message 'PSGalleryExplorer was unable to source the required data set file.' 57 | Write-Warning -Message 'Ensure you have an active internet connection' 58 | return 59 | } #else_Import-XMLDataSet 60 | 61 | Write-Verbose -Message 'Adding output properties to objects...' 62 | foreach ($item in $find) { 63 | $metrics = $null 64 | $metrics = @{ 65 | Downloads = $item.AdditionalMetadata.downloadCount 66 | LastUpdate = $item.AdditionalMetadata.lastUpdated 67 | Star = $item.ProjectInfo.StarCount 68 | Sub = $item.ProjectInfo.Subscribers 69 | Watch = $item.ProjectInfo.Watchers 70 | Fork = $item.ProjectInfo.Forks 71 | Issues = $item.ProjectInfo.Issues 72 | RepoUpdate = $item.ProjectInfo.Updated 73 | } 74 | $item | Add-Member -NotePropertyMembers $metrics -TypeName Asset -Force 75 | if ($InsightView) { 76 | $item.PSObject.TypeNames.Insert(0, 'PSGEInsight') 77 | } 78 | else { 79 | $item.PSObject.TypeNames.Insert(0, 'PSGEFormat') 80 | } 81 | } #foreach_find 82 | Write-Verbose -Message 'Properties addition completed.' 83 | 84 | return $find 85 | } #Find-ModuleByCommand 86 | -------------------------------------------------------------------------------- /src/PSScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | #________________________________________ 3 | #IncludeDefaultRules 4 | IncludeDefaultRules = $true 5 | #________________________________________ 6 | #Severity 7 | #Specify Severity when you want to limit generated diagnostic records to a specific subset: [ Error | Warning | Information ] 8 | Severity = @('Error', 'Warning') 9 | #________________________________________ 10 | #CustomRulePath 11 | #Specify CustomRulePath when you have a large set of custom rules you'd like to reference 12 | #CustomRulePath = "Module\InjectionHunter\1.0.0\InjectionHunter.psd1" 13 | #________________________________________ 14 | #IncludeRules 15 | #Specify IncludeRules when you only want to run specific subset of rules instead of the default rule set. 16 | #IncludeRules = @('PSShouldProcess', 17 | # 'PSUseApprovedVerbs') 18 | #________________________________________ 19 | #ExcludeRules 20 | #Specify ExcludeRules when you want to exclude a certain rule from the the default set of rules. 21 | #ExcludeRules = @( 22 | # 'PSUseDeclaredVarsMoreThanAssignments' 23 | #) 24 | #________________________________________ 25 | #Rules 26 | #Here you can specify customizations for particular rules. Several examples are included below: 27 | #Rules = @{ 28 | # PSUseCompatibleCmdlets = @{ 29 | # compatibility = @('core-6.1.0-windows', 'desktop-4.0-windows') 30 | # } 31 | # PSUseCompatibleSyntax = @{ 32 | # Enable = $true 33 | # TargetVersions = @( 34 | # '3.0', 35 | # '5.1', 36 | # '6.2' 37 | # ) 38 | # } 39 | # PSUseCompatibleCommands = @{ 40 | # Enable = $true 41 | # TargetProfiles = @( 42 | # 'win-8_x64_10.0.14393.0_6.1.3_x64_4.0.30319.42000_core', # PS 6.1 on WinServer-2019 43 | # 'win-8_x64_10.0.17763.0_5.1.17763.316_x64_4.0.30319.42000_framework', # PS 5.1 on WinServer-2019 44 | # 'win-8_x64_6.2.9200.0_3.0_x64_4.0.30319.42000_framework' # PS 3 on WinServer-2012 45 | # ) 46 | # } 47 | # PSUseCompatibleTypes = @{ 48 | # Enable = $true 49 | # TargetProfiles = @( 50 | # 'ubuntu_x64_18.04_6.1.3_x64_4.0.30319.42000_core', 51 | # 'win-48_x64_10.0.17763.0_5.1.17763.316_x64_4.0.30319.42000_framework' 52 | # ) 53 | # # You can specify types to not check like this, which will also ignore methods and members on it: 54 | # IgnoreTypes = @( 55 | # 'System.IO.Compression.ZipFile' 56 | # ) 57 | # } 58 | #} 59 | #________________________________________ 60 | } 61 | -------------------------------------------------------------------------------- /src/Tests/Unit/ExportedFunctions.Tests.ps1: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | Set-Location -Path $PSScriptRoot 3 | #------------------------------------------------------------------------- 4 | $ModuleName = 'PSGalleryExplorer' 5 | $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") 6 | #------------------------------------------------------------------------- 7 | if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { 8 | #if the module is already in memory, remove it 9 | Remove-Module -Name $ModuleName -Force 10 | } 11 | Import-Module $PathToManifest -Force 12 | #------------------------------------------------------------------------- 13 | 14 | BeforeAll { 15 | Set-Location -Path $PSScriptRoot 16 | $ModuleName = 'PSGalleryExplorer' 17 | $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") 18 | $manifestContent = Test-ModuleManifest -Path $PathToManifest 19 | $moduleExported = Get-Command -Module $ModuleName | Select-Object -ExpandProperty Name 20 | $manifestExported = ($manifestContent.ExportedFunctions).Keys 21 | } 22 | Describe $ModuleName { 23 | 24 | Context 'Exported Commands' -Fixture { 25 | 26 | Context 'Number of commands' -Fixture { 27 | It -Name 'Exports the same number of public functions as what is listed in the Module Manifest' -Test { 28 | $manifestExported.Count | Should -BeExactly $moduleExported.Count 29 | } 30 | } 31 | 32 | Context 'Explicitly exported commands' -ForEach $moduleExported { 33 | foreach ($command in $moduleExported) { 34 | BeforeAll { 35 | $command = $_ 36 | } 37 | It -Name "Includes the $command in the Module Manifest ExportedFunctions" -Test { 38 | $manifestExported -contains $command | Should -BeTrue 39 | } 40 | } 41 | } 42 | } 43 | 44 | Context 'Command Help' -ForEach $moduleExported { 45 | foreach ($command in $moduleExported) { 46 | BeforeAll { 47 | $help = Get-Help -Name $_ -Full 48 | } 49 | Context $command -Fixture { 50 | $help = Get-Help -Name $command -Full 51 | 52 | It -Name 'Includes a Synopsis' -Test { 53 | $help.Synopsis | Should -Not -BeNullOrEmpty 54 | } 55 | 56 | It -Name 'Includes a Description' -Test { 57 | $help.description.Text | Should -Not -BeNullOrEmpty 58 | } 59 | 60 | It -Name 'Includes an Example' -Test { 61 | $help.examples.example | Should -Not -BeNullOrEmpty 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Tests/Unit/PSGalleryExplorer-Module.Tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll { 2 | #------------------------------------------------------------------------- 3 | Set-Location -Path $PSScriptRoot 4 | #------------------------------------------------------------------------- 5 | $ModuleName = 'PSGalleryExplorer' 6 | $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") 7 | $PathToModule = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psm1") 8 | #------------------------------------------------------------------------- 9 | } 10 | Describe 'Module Tests' -Tag Unit { 11 | Context "Module Tests" { 12 | $script:manifestEval = $null 13 | It 'Passes Test-ModuleManifest' { 14 | { $script:manifestEval = Test-ModuleManifest -Path $PathToManifest } | Should -Not -Throw 15 | $? | Should -BeTrue 16 | } #manifestTest 17 | It 'root module PSGalleryExplorer.psm1 should exist' { 18 | $PathToModule | Should -Exist 19 | $? | Should -BeTrue 20 | } #psm1Exists 21 | It 'manifest should contain PSGalleryExplorer.psm1' { 22 | $PathToManifest | 23 | Should -FileContentMatchExactly "PSGalleryExplorer.psm1" 24 | } #validPSM1 25 | It 'should have a matching module name in the manifest' { 26 | $script:manifestEval.Name | Should -BeExactly $ModuleName 27 | } #name 28 | It 'should have a valid description in the manifest' { 29 | $script:manifestEval.Description | Should -Not -BeNullOrEmpty 30 | } #description 31 | It 'should have a valid author in the manifest' { 32 | $script:manifestEval.Author | Should -Not -BeNullOrEmpty 33 | } #author 34 | It 'should have a valid version in the manifest' { 35 | $script:manifestEval.Version -as [Version] | Should -Not -BeNullOrEmpty 36 | } #version 37 | It 'should have a valid guid in the manifest' { 38 | { [guid]::Parse($script:manifestEval.Guid) } | Should -Not -Throw 39 | } #guid 40 | It 'should not have any spaces in the tags' { 41 | foreach ($tag in $script:manifestEval.Tags) { 42 | $tag | Should -Not -Match '\s' 43 | } 44 | } #tagSpaces 45 | It 'should have a valid project Uri' { 46 | $script:manifestEval.ProjectUri | Should -Not -BeNullOrEmpty 47 | } #uri 48 | } #context_ModuleTests 49 | } #describe_ModuleTests 50 | -------------------------------------------------------------------------------- /src/Tests/Unit/Private/Confirm-DataLocation.Tests.ps1: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | Set-Location -Path $PSScriptRoot 3 | #------------------------------------------------------------------------- 4 | $ModuleName = 'PSGalleryExplorer' 5 | $PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1") 6 | #------------------------------------------------------------------------- 7 | if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { 8 | #if the module is already in memory, remove it 9 | Remove-Module -Name $ModuleName -Force 10 | } 11 | Import-Module $PathToManifest -Force 12 | #------------------------------------------------------------------------- 13 | 14 | InModuleScope 'PSGalleryExplorer' { 15 | 16 | Describe 'Confirm-DataLocation' -Tag Unit { 17 | BeforeAll { 18 | $WarningPreference = 'SilentlyContinue' 19 | $ErrorActionPreference = 'SilentlyContinue' 20 | } #before_all 21 | 22 | BeforeEach { 23 | Mock -CommandName Test-Path -MockWith { 24 | $true 25 | } #endMock 26 | Mock -CommandName New-Item -MockWith { } #endMock 27 | } #before_each 28 | 29 | Context 'Error' { 30 | 31 | It 'should return false if an error is encountered with Test-Path' { 32 | Mock -CommandName Test-Path -MockWith { 33 | throw 'FakeError' 34 | } #endMock 35 | Confirm-DataLocation | Should -BeExactly $false 36 | } #it 37 | 38 | It 'should return false if an error is encountered with New-Item' { 39 | Mock -CommandName Test-Path -MockWith { 40 | $false 41 | } #endMock 42 | Mock -CommandName New-Item -MockWith { 43 | throw 'FakeError' 44 | } #endMock 45 | Confirm-DataLocation | Should -BeExactly $false 46 | } #it 47 | 48 | } #context_Error 49 | 50 | Context 'Success' { 51 | 52 | It 'should return true if the output dir already exists' { 53 | Confirm-DataLocation | Should -BeExactly $true 54 | } #it 55 | 56 | It 'should return true if the output dir does not exists and is created' { 57 | Mock -CommandName Test-Path -MockWith { 58 | $false 59 | } #endMock 60 | Confirm-DataLocation | Should -BeExactly $true 61 | } #it 62 | 63 | } #context_Success 64 | 65 | } #describe_Confirm-DataLocation 66 | 67 | } #inModule 68 | -------------------------------------------------------------------------------- /src/Tests/Unit/Private/Confirm-MetadataUpdate.Tests.ps1: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | Set-Location -Path $PSScriptRoot 3 | #------------------------------------------------------------------------- 4 | $ModuleName = 'PSGalleryExplorer' 5 | $PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1") 6 | #------------------------------------------------------------------------- 7 | if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { 8 | #if the module is already in memory, remove it 9 | Remove-Module -Name $ModuleName -Force 10 | } 11 | Import-Module $PathToManifest -Force 12 | #------------------------------------------------------------------------- 13 | 14 | InModuleScope 'PSGalleryExplorer' { 15 | 16 | Describe 'Confirm-MetadataUpdate' -Tag Unit { 17 | BeforeAll { 18 | $WarningPreference = 'SilentlyContinue' 19 | $ErrorActionPreference = 'SilentlyContinue' 20 | $metadataJSON = @' 21 | { 22 | "zipCreated": "1679500816" 23 | } 24 | '@ 25 | $metadataJSON2 = @' 26 | { 27 | "zipCreated": "1679500817" 28 | } 29 | '@ 30 | function Remove-Item { 31 | } 32 | } #before_all 33 | 34 | BeforeEach { 35 | Mock -CommandName Test-Path -MockWith { 36 | $true 37 | } #endMock 38 | Mock -CommandName Get-Content -MockWith { 39 | $metadataJSON 40 | } #endMock 41 | Mock -CommandName Remove-Item -MockWith {} 42 | Mock -CommandName Get-RemoteFile -MockWith { 43 | $true 44 | } #endMock 45 | } #before_each 46 | 47 | Context 'Error' { 48 | 49 | It 'should return false if an error is encountered with Test-Path' { 50 | Mock -CommandName Test-Path -MockWith { 51 | throw 'FakeError' 52 | } #endMock 53 | Confirm-MetadataUpdate | Should -BeExactly $false 54 | } #it 55 | 56 | It 'should return false if an error is encountered with Get-Content' { 57 | Mock -CommandName Get-Content -MockWith { 58 | throw 'FakeError' 59 | } #endMock 60 | Confirm-MetadataUpdate | Should -BeExactly $false 61 | } #it 62 | 63 | It 'should return false if the metadata file is not downloaded' { 64 | Mock -CommandName Get-RemoteFile -MockWith { 65 | $false 66 | } #endMock 67 | Confirm-MetadataUpdate | Should -BeExactly $false 68 | } #it 69 | 70 | It 'should return false if an error is encountered with second Get-Content' { 71 | $script:mockCalled = 0 72 | $mockInvoke = { 73 | $script:mockCalled++ 74 | if ($script:mockCalled -eq 1) { 75 | return $metadataJSON 76 | } 77 | elseif ($script:mockCalled -eq 2) { 78 | throw 'FakeError' 79 | } 80 | } 81 | Mock -CommandName Get-Content -MockWith $mockInvoke 82 | Confirm-MetadataUpdate | Should -BeExactly $false 83 | } #it 84 | 85 | } #context_Error 86 | 87 | Context 'Success' { 88 | 89 | It 'should return false if the metadata file is not found' { 90 | Mock -CommandName Test-Path -MockWith { 91 | $false 92 | } #endMock 93 | Confirm-MetadataUpdate | Should -BeExactly $false 94 | } #it 95 | 96 | It 'should return true if the local and remote metadata files are the same' { 97 | Confirm-MetadataUpdate | Should -BeExactly $true 98 | } #it 99 | 100 | It 'should return false if the local and remote metadata files are different' { 101 | $script:mockCalled = 0 102 | $mockInvoke = { 103 | $script:mockCalled++ 104 | if ($script:mockCalled -eq 1) { 105 | return $metadataJSON 106 | } 107 | elseif ($script:mockCalled -eq 2) { 108 | return $metadataJSON2 109 | } 110 | } 111 | Mock -CommandName Get-Content -MockWith $mockInvoke 112 | Confirm-MetadataUpdate | Should -BeExactly $false 113 | } #it 114 | 115 | } #context_Success 116 | 117 | } #describe_Confirm-MetadataUpdate 118 | 119 | } #inModule 120 | -------------------------------------------------------------------------------- /src/Tests/Unit/Private/Confirm-XMLDataSet.Tests.ps1: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | Set-Location -Path $PSScriptRoot 3 | #------------------------------------------------------------------------- 4 | $ModuleName = 'PSGalleryExplorer' 5 | $PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1") 6 | #------------------------------------------------------------------------- 7 | if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { 8 | #if the module is already in memory, remove it 9 | Remove-Module -Name $ModuleName -Force 10 | } 11 | Import-Module $PathToManifest -Force 12 | #------------------------------------------------------------------------- 13 | 14 | InModuleScope 'PSGalleryExplorer' { 15 | 16 | Describe 'Confirm-XMLDataSet' -Tag Unit { 17 | BeforeAll { 18 | $WarningPreference = 'SilentlyContinue' 19 | $ErrorActionPreference = 'SilentlyContinue' 20 | } #before_all 21 | 22 | BeforeEach { 23 | Mock -CommandName Test-Path -MockWith { 24 | $true 25 | } #endMock 26 | Mock -CommandName Get-Item -MockWith { 27 | [PSCustomObject]@{ 28 | Name = 'AzModI.xml' 29 | CreationTime = [datetime]'01/06/20 21:17:21' 30 | CreationTimeUtc = [datetime]'01/07/20 05:17:21' 31 | LastAccessTime = [datetime]'01/06/20 21:17:22' 32 | LastWriteTime = [datetime]'01/06/20 21:17:21' 33 | } 34 | } #endMock 35 | Mock -CommandName Get-Date -MockWith { 36 | [datetime]'01/06/20 21:17:22' 37 | } #endMock 38 | Mock -CommandName Confirm-MetadataUpdate -MockWith { 39 | $true 40 | } #endMock 41 | } #before_each 42 | 43 | Context 'Error' { 44 | 45 | It 'should return false if an error is encountered with Test-Path' { 46 | Mock -CommandName Test-Path -MockWith { 47 | throw 'FakeError' 48 | } #endMock 49 | Confirm-XMLDataSet | Should -BeExactly $false 50 | } #it 51 | 52 | It 'should return false if an error is encountered with Get-Item' { 53 | Mock -CommandName Get-Item -MockWith { 54 | throw 'FakeError' 55 | } #endMock 56 | Confirm-XMLDataSet | Should -BeExactly $false 57 | } #it 58 | 59 | It 'should return null false if no file information is returned from Get-Item' { 60 | Mock -CommandName Get-Item -MockWith { } #endMock 61 | Confirm-XMLDataSet | Should -BeExactly $false 62 | } #it 63 | 64 | } #context_Error 65 | 66 | Context 'Success' { 67 | 68 | It 'should return false if the data file is not found' { 69 | Mock -CommandName Test-Path -MockWith { 70 | $false 71 | } #endMock 72 | Confirm-XMLDataSet | Should -BeExactly $false 73 | } #it 74 | 75 | It 'should return true if the file is found and is less than 9 days old' { 76 | Confirm-XMLDataSet | Should -BeExactly $true 77 | } #it 78 | 79 | It 'should return true if the data file is older than 9 days but the metadata file is current' { 80 | Mock -CommandName Get-Date -MockWith { 81 | [datetime]'1/16/20 21:17:22' 82 | } #endMock 83 | Confirm-XMLDataSet | Should -BeExactly $true 84 | } #it 85 | 86 | It 'should return false if the data file is older than 9 days and the metadata file is also out of date' { 87 | Mock -CommandName Get-Date -MockWith { 88 | [datetime]'1/16/20 21:17:22' 89 | } #endMock 90 | Mock -CommandName Confirm-MetadataUpdate -MockWith { 91 | $false 92 | } #endMock 93 | Confirm-XMLDataSet | Should -BeExactly $false 94 | } #it 95 | 96 | It 'should check the metadata file if the data file is greater than 9 days old' { 97 | Mock -CommandName Get-Date -MockWith { 98 | [datetime]'1/16/20 21:17:22' 99 | } #endMock 100 | Confirm-XMLDataSet 101 | Should -Invoke -CommandName Confirm-MetadataUpdate -Exactly 1 102 | } #it 103 | 104 | } #context_Success 105 | 106 | } #describe_Confirm-XMLDataSet 107 | 108 | } #inModule 109 | -------------------------------------------------------------------------------- /src/Tests/Unit/Private/Expand-XMLDataSet.Tests.ps1: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | Set-Location -Path $PSScriptRoot 3 | #------------------------------------------------------------------------- 4 | $ModuleName = 'PSGalleryExplorer' 5 | $PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1") 6 | #------------------------------------------------------------------------- 7 | if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { 8 | #if the module is already in memory, remove it 9 | Remove-Module -Name $ModuleName -Force 10 | } 11 | Import-Module $PathToManifest -Force 12 | #------------------------------------------------------------------------- 13 | 14 | InModuleScope 'PSGalleryExplorer' { 15 | 16 | Describe 'Expand-XMLDataSet' -Tag Unit { 17 | BeforeAll { 18 | $WarningPreference = 'SilentlyContinue' 19 | $ErrorActionPreference = 'SilentlyContinue' 20 | } #before_all 21 | 22 | BeforeEach { 23 | Mock -CommandName Test-Path -MockWith { 24 | $false 25 | } #endMock 26 | Mock -CommandName Remove-Item -MockWith { 27 | 28 | } #endMock 29 | Mock -CommandName Expand-Archive -MockWith { 30 | [PSCustomObject]@{ 31 | Name = 'AzModI.xml' 32 | Exists = 'True' 33 | CreationTime = [datetime]'01 / 06 / 20 23:02:16' 34 | } 35 | } #endMock 36 | } #before_each 37 | 38 | Context 'Error' { 39 | 40 | It 'should return false if an error is encountered with Test-Path' { 41 | Mock -CommandName Test-Path -MockWith { 42 | throw 'FakeError' 43 | } #endMock 44 | Expand-XMLDataSet | Should -BeExactly $false 45 | } #it 46 | 47 | It 'should return false if an error is encountered with Remove-Item' { 48 | Mock -CommandName Test-Path -MockWith { 49 | $true 50 | } #endMock 51 | Mock -CommandName Remove-Item -MockWith { 52 | throw 'FakeError' 53 | } #endMock 54 | Expand-XMLDataSet | Should -BeExactly $false 55 | } #it 56 | 57 | It 'should return false if an error is encountered with Expand-Archive' { 58 | Mock -CommandName Expand-Archive -MockWith { 59 | throw 'FakeError' 60 | } #endMock 61 | Expand-XMLDataSet | Should -BeExactly $false 62 | } #it 63 | 64 | } #context_Error 65 | 66 | Context 'Success' { 67 | 68 | It 'should return true if the data file is expanded' { 69 | Expand-XMLDataSet | Should -BeExactly $true 70 | } #it 71 | 72 | } #context_Success 73 | 74 | } #describe_Expand-XMLDataSet 75 | 76 | } #inModule 77 | -------------------------------------------------------------------------------- /src/Tests/Unit/Private/Get-RemoteFile.Tests.ps1: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | Set-Location -Path $PSScriptRoot 3 | #------------------------------------------------------------------------- 4 | $ModuleName = 'PSGalleryExplorer' 5 | $PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1") 6 | #------------------------------------------------------------------------- 7 | if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { 8 | #if the module is already in memory, remove it 9 | Remove-Module -Name $ModuleName -Force 10 | } 11 | Import-Module $PathToManifest -Force 12 | #------------------------------------------------------------------------- 13 | 14 | InModuleScope 'PSGalleryExplorer' { 15 | 16 | Describe 'Get-RemoteFile' -Tag Unit { 17 | BeforeAll { 18 | $WarningPreference = 'SilentlyContinue' 19 | $ErrorActionPreference = 'SilentlyContinue' 20 | $fileName = 'test.zip' 21 | } #before_all 22 | 23 | BeforeEach { 24 | Mock -CommandName Invoke-WebRequest -MockWith { 25 | [PSCustomObject]@{ 26 | StatusCode = '200' 27 | StatusDescription = 'OK' 28 | Content = '{80, 75, 3, 4}' 29 | } 30 | } #endMock 31 | } #before_each 32 | 33 | Context 'Error' { 34 | 35 | It 'should return false if an error is encountered downloading the file' { 36 | Mock -CommandName Invoke-WebRequest -MockWith { 37 | throw 'FakeError' 38 | } #endMock 39 | Get-RemoteFile -File $fileName | Should -BeExactly $false 40 | } #it 41 | 42 | } #context_FunctionName 43 | 44 | Context 'Success' { 45 | 46 | It 'should return true if the file is downloaded' { 47 | Get-RemoteFile -File $fileName | Should -BeExactly $true 48 | } #it 49 | 50 | It 'should call Invoke-WebRequest with the correct parameters' { 51 | $outPath = [System.IO.Path]::Combine($script:dataPath, $fileName) 52 | Get-RemoteFile -File $fileName 53 | Should -Invoke -CommandName Invoke-WebRequest -Times 1 -Exactly -Scope It -ParameterFilter { 54 | $OutFile -eq $outPath -and $Uri -eq "https://$script:dlURI/$fileName" 55 | } 56 | } #it 57 | 58 | It 'should call Invoke-WebRequest with the correct parameters if OutFileName is specified' { 59 | $fileNameOverride = 'test2.zip' 60 | $outPath = [System.IO.Path]::Combine($script:dataPath, $fileNameOverride) 61 | Get-RemoteFile -File $fileName -OutFileName $fileNameOverride 62 | Should -Invoke -CommandName Invoke-WebRequest -Times 1 -Exactly -Scope It -ParameterFilter { 63 | $OutFile -eq $outPath -and $Uri -eq "https://$script:dlURI/$fileName" 64 | } 65 | } #it 66 | 67 | } #context_Success 68 | 69 | } #describe_Get-RemoteFile 70 | 71 | } #inModule 72 | -------------------------------------------------------------------------------- /src/Tests/Unit/Private/Invoke-XMLDataCheck.Tests.ps1: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | Set-Location -Path $PSScriptRoot 3 | #------------------------------------------------------------------------- 4 | $ModuleName = 'PSGalleryExplorer' 5 | $PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1") 6 | #------------------------------------------------------------------------- 7 | if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { 8 | #if the module is already in memory, remove it 9 | Remove-Module -Name $ModuleName -Force 10 | } 11 | Import-Module $PathToManifest -Force 12 | #------------------------------------------------------------------------- 13 | 14 | InModuleScope 'PSGalleryExplorer' { 15 | 16 | Describe 'Invoke-XMLDataCheck' -Tag Unit { 17 | BeforeAll { 18 | $WarningPreference = 'SilentlyContinue' 19 | $ErrorActionPreference = 'SilentlyContinue' 20 | function Confirm-DataLocation { 21 | } 22 | function Confirm-XMLDataSet { 23 | } 24 | function Expand-XMLDataSet { 25 | } 26 | function Get-RemoteFile { 27 | } 28 | function Remove-Item { 29 | } 30 | } #before_all 31 | 32 | Context 'ShouldProcess' { 33 | BeforeEach { 34 | Mock -CommandName Invoke-XMLDataCheck -MockWith { } #endMock 35 | } #before_each 36 | It 'Should process by default' { 37 | Invoke-XMLDataCheck 38 | Should -Invoke Invoke-XMLDataCheck -Scope It -Exactly -Times 1 39 | } #it 40 | It 'Should not process on explicit request for confirmation (-Confirm)' { 41 | { Invoke-XMLDataCheck -Confirm } 42 | Should -Invoke Invoke-XMLDataCheck -Scope It -Exactly -Times 0 43 | } #it 44 | It 'Should not process on implicit request for confirmation (ConfirmPreference)' { 45 | { 46 | $ConfirmPreference = 'Low' 47 | Invoke-XMLDataCheck 48 | } 49 | Should -Invoke Invoke-XMLDataCheck -Scope It -Exactly -Times 0 50 | } #it 51 | It 'Should not process on explicit request for validation (-WhatIf)' { 52 | { Invoke-XMLDataCheck -WhatIf } 53 | Should -Invoke Invoke-XMLDataCheck -Scope It -Exactly -Times 0 54 | } #it 55 | It 'Should not process on implicit request for validation (WhatIfPreference)' { 56 | { 57 | $WhatIfPreference = $true 58 | Invoke-XMLDataCheck 59 | } 60 | Should -Invoke Invoke-XMLDataCheck -Scope It -Exactly -Times 0 61 | } #it 62 | It 'Should process on force' { 63 | $ConfirmPreference = 'Medium' 64 | Invoke-XMLDataCheck -Force 65 | Should -Invoke Invoke-XMLDataCheck -Scope It -Exactly -Times 1 66 | } #it 67 | } #context_ShouldProcess 68 | 69 | BeforeEach { 70 | Mock -CommandName Confirm-DataLocation -MockWith { 71 | $true 72 | } #endMock 73 | Mock -CommandName Confirm-XMLDataSet -MockWith { 74 | $true 75 | } #endMock 76 | Mock -CommandName Expand-XMLDataSet -MockWith { 77 | $true 78 | } #endMock 79 | Mock -CommandName Get-RemoteFile -MockWith { 80 | $true 81 | } #endMock 82 | Mock -CommandName Test-Path -MockWith { 83 | $true 84 | } #endMock 85 | Mock -CommandName Remove-Item -MockWith {} 86 | } #before_each 87 | 88 | Context 'Success' { 89 | 90 | It 'should return true if the data file is confirmed' { 91 | Invoke-XMLDataCheck -Force | Should -BeExactly $true 92 | } #it 93 | 94 | It 'should return false if the data output dir cannot be confirmed' { 95 | Mock -CommandName Confirm-DataLocation -MockWith { 96 | $false 97 | } #endMock 98 | Invoke-XMLDataCheck -Force | Should -BeExactly $false 99 | } #it 100 | 101 | It 'should return false if the data file is not confirmed and the file can not be downloaded' { 102 | Mock -CommandName Confirm-XMLDataSet -MockWith { 103 | $false 104 | } #endMock 105 | Mock -CommandName Get-RemoteFile -MockWith { 106 | $false 107 | } #endMock 108 | Invoke-XMLDataCheck -Force | Should -BeExactly $false 109 | } #it 110 | 111 | It 'should return false if the data file is not confirmed and the file is downloaded, but can not be expanded' { 112 | Mock -CommandName Confirm-XMLDataSet -MockWith { 113 | $false 114 | } #endMock 115 | Mock -CommandName Expand-XMLDataSet -MockWith { 116 | $false 117 | } #endMock 118 | Invoke-XMLDataCheck -Force | Should -BeExactly $false 119 | } #it 120 | 121 | It 'should return true if the data file is not confirmed and the file is downloaded and expanded' { 122 | Mock -CommandName Confirm-XMLDataSet -MockWith { 123 | $false 124 | } #endMock 125 | Invoke-XMLDataCheck -Force | Should -BeExactly $true 126 | } #it 127 | 128 | It 'should download 2 files if the data file is not confirmed' { 129 | Mock -CommandName Confirm-XMLDataSet -MockWith { 130 | $false 131 | } #endMock 132 | Invoke-XMLDataCheck -Force 133 | Should -Invoke Get-RemoteFile -Scope It -Exactly -Times 2 134 | } #it 135 | 136 | It 'should not attempt to remove the metadata file if it does not exist' { 137 | Mock -CommandName Confirm-XMLDataSet -MockWith { 138 | $false 139 | } #endMock 140 | Mock -CommandName Test-Path -MockWith { 141 | $false 142 | } #endMock 143 | Invoke-XMLDataCheck -Force 144 | Should -Invoke Remove-Item -Scope It -Exactly -Times 0 145 | } #it 146 | 147 | } #context_Success 148 | 149 | } #describe_Invoke-XMLDataCheck 150 | 151 | } #inModule 152 | --------------------------------------------------------------------------------