├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── check-versions-quicksight.md └── images │ ├── 01.png │ ├── 100.png │ ├── 110.png │ ├── 120.png │ ├── 130.png │ ├── 140.png │ ├── 150.png │ ├── 160.png │ ├── 170.png │ ├── 180.png │ ├── 190.png │ ├── 20.png │ ├── 200.png │ ├── 210.png │ ├── 220.png │ ├── 230.png │ ├── 240.png │ ├── 250.png │ ├── 260.png │ ├── 270.png │ ├── 292.png │ ├── 293.png │ ├── 294.png │ ├── 295.png │ ├── 296.png │ ├── 297.png │ ├── 298.png │ ├── 299.png │ ├── 30.png │ ├── 300.png │ ├── 310.png │ ├── 320.png │ ├── 330.png │ ├── 340.png │ ├── 350.png │ ├── 360.png │ ├── 370.png │ ├── 380.png │ ├── 40.png │ ├── 50.png │ ├── 60.png │ ├── 70.png │ ├── 80.png │ └── 90.png ├── events └── event.json ├── invoke_version_step ├── __init__.py ├── app.py └── requirements.txt ├── stackset-role-template.yaml ├── template.yaml ├── tests └── unit │ ├── __init__.py │ └── test_handler.py ├── version_acct_list ├── __init__.py ├── accounts.csv ├── app.py └── requirements.txt ├── version_combine ├── __init__.py ├── app.py └── requirements.txt ├── version_combine_acct ├── __init__.py ├── app.py └── requirements.txt ├── version_docdb ├── __init__.py ├── app.py └── requirements.txt ├── version_ec ├── __init__.py ├── app.py └── requirements.txt ├── version_eks ├── __init__.py ├── app.py └── requirements.txt ├── version_es ├── __init__.py ├── app.py └── requirements.txt ├── version_mq ├── __init__.py ├── app.py └── requirements.txt ├── version_msk ├── __init__.py ├── app.py └── requirements.txt ├── version_rds ├── __init__.py ├── app.py └── requirements.txt └── version_state_machine └── version_state_machine.asl.json /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | ## Contributing via Pull Requests 23 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that you are working against the latest source on the `dev` branch. 24 | 25 | Please keep in mind the following before sending a pull request. 26 | 27 | - You are working against the latest source on the `dev` branch. 28 | - You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 29 | - You open an issue to discuss any significant work - we would hate for your time to be wasted. 30 | 31 | To send us a pull request, please: 32 | 33 | 1. Fork the repository. 34 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | 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 (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Check Versions Dashboard 2 | 3 | **Status**: Initial MVP 4 | The AWS Check Versions Dashboard project contains source code and documentation for backend code and an Amazon Quicksight dashboard to view the deployed versions for Amazon RDS instances, Amazon Managed Streaming for Apache (MSK), Amazon MQ, Amazon Elasticsearch clusters, Amazon EKS clusters, Amazon ElastiCache clusters, and Amazon DocumentDB (DocDB) clusters vs. the latest available versions for each service. 5 | 6 | The AWS Check Version Dashboard provides two templates to launch the required resources. You can leverage CloudFormation StackSets to launch ```stackset-role-template.yaml``` into your Organizations child accounts that will create the necessary IAM roles Lambda functions will assume for cross-account access. The solution also includes an AWS SAM (Serverless Application Model) template ```template.yaml``` that will launch all of the necessary resources into an administrator or delegated administrator account. 7 | 8 | Example Dashboard using Amazon Quicksight: 9 | ![Example Dashboard](./docs/images/170.png) 10 | 11 | 12 | **Deployment Instructions** for ```stackset-role-template.yaml``` 13 | 14 | **Required parameters**: 15 | 16 | * **PrimaryAccountId**: The current administrator or delegated administrator account you are launching the StackSet out of. 17 | 18 | To deploy this template via CloudFormation StackSets, please refer to the CloudFormation StackSet documentation available at this url: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-getting-started-create.html 19 | 20 | To deploy this template to your preferred AWS Organization child account(s) and region(s) via CloudFormation Stack, please refer to the CloudFormation documentation available at this url: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-console-create-stack.html 21 | 22 | 23 | **Deployment Instructions** for ```template.yaml``` 24 | Next, you can follow the instructions below to build and deploy an AWS SAM (Serverless Application Model) template ```template.yaml``` that will launch all of the necessary resources into an administrator or delegated administrator account. 25 | 26 | ## Build application 27 | 28 | The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. 29 | 30 | To use the SAM CLI, you need the following tools. 31 | 32 | * SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) 33 | * [Python 3 installed](https://www.python.org/downloads/) 34 | * Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) 35 | 36 | To build and deploy the application: run the following in your shell: 37 | 38 | ```bash 39 | sam build 40 | sam deploy --guided --capabilities CAPABILITY_NAMED_IAM 41 | ``` 42 | 43 | The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: 44 | 45 | * **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be check-version-dashboard. 46 | * **AWS Region**: The AWS region you want to deploy your app to. 47 | * **SpecifyAccountsOrEnterAll**: Enter an Account ID, a comma delimited list of Account IDs, enter ALL for an entire AWS Organization, or replace the accounts.csv file in the version_acct_list Lambda directory with a list of comma delimited accounts 48 | * **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. 49 | * **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modified IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. 50 | * **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. 51 | 52 | You can find your S3 bucket for the Amazon Quicksight dataset in the output values displayed after deployment. 53 | 54 | # Architecture 55 | 56 | This application will deploy the following backend resources: 57 | * An S3 Bucket to host the data in a csv 58 | * A Step Function to coordinate all Lambda functions 59 | * An Event Bridge cron event to trigger the Step Function once a week on Friday Morning. 60 | * 8 Lambda functions that will capture the data for each service and combine into a single csv for Quicksight 61 | * 1 Lambda function that can trigger the step function on demand via updates to the cloudformation template 62 | * Associated IAM permissions for Lambda Functions to make appropriate API calls and for each service in the architecture to communicate with the other service. 63 | 64 | # Build Quicksight Dashboard 65 | 66 | To build the QuickSight Dashboard, please refer to: [Build Dashboard](docs/check-versions-quicksight.md) 67 | 68 | # Estimated costs: 69 | 70 | Expenses related to this build are estimates only and are based on your specific deployment, amount of data collected and region: 71 | For us-east-1: 72 | 73 | 1. S3 file: likely less than 1 MB: < $0.000023/mo 74 | 2. Step Functions: 4 transitions per month: included in Free Tier 75 | 3. Lambda functions: 32 Invokes per month: included in Free Tier 76 | 4. Event bridge: 4 Cron events per month: Free 77 | 5. Quicksight: Dependant on the number of authors and readers. At least 1 author is needed to build dashboard See [Amazon Quicksight Pricing](https://aws.amazon.com/quicksight/pricing/) 78 | -------------------------------------------------------------------------------- /docs/check-versions-quicksight.md: -------------------------------------------------------------------------------- 1 | Build a Check Version Dashboard in AWS QuickSight 2 | ================================================= 3 | 4 | Overview 5 | -------- 6 | 7 | This document outlines a process for building meaningful dashboards to visualize the version data generated by the AWS Check Versions solution. There is no firm definition for the dashboard. This guide only suggests practical techniques to begin the process of making the most of your data. Feel free to adapt your visuals for your own needs. While the ideal solution is to provide a distributable template to create these dashboards automatically, as per an AWS CloudFormation template, AWS QuickSight does not currently support such functionality. Please refer to this guide in lieu of such automation capability is available. 8 | 9 | The full scope of visualizations in this guide include: a summary dashboard as an overview of all the services that are checked by the solution as well as five detail dashboards pages that drill into each of the five checked services. For brevity, the summary page and one example of a service check detail page will be shown. You should apply as necessary the techniques to create the detail pages for the other service checks available from the core solution. 10 | 11 | There are two essential aspects to address in this guide: 12 | 1. Establish a Data Set in QuickSight that links the output of the core Check Versions code with the dashboards you create to visualize it. 13 | 1. Create the dashboard visualizations from the Data Set. 14 | 15 | _Assumptions: To deploy the dashboard solution described in this document, the user should have already installed the core code portion of this solution. The user should also posess some basic understanding of visualizing data with graphs. Prior AWS QuickSight experience is helpful but not required. Please refer to https://docs.aws.amazon.com/quicksight/ for additional guidance._ 16 | 17 | Step 1: Establish the Dataset 18 | -------- 19 | 20 | In this step you will: 21 | * Grant permission for QuickSight to access the S3 bucket created via the installation stack 22 | * Add a data set based on the file produced by the solution 23 | * Create a schedule for automatic data refresh from the S3 source (Optional) 24 | 25 | ### _Grant permission for QuickSight to access your S3 bucket_ 26 | Begin by launching AWS QuickSight from your web Console. 27 | 28 | ![Launch QuickSight](./images/299.png) 29 | 30 | QuickSight requires permission to access S3 bucket. From the QuickSight home screen, make sure you are in the same region into which you deployed your solution. Select your user icon from the top right corner and select "Manage QuickSight." 31 | 32 | ![Manage QuickSight](./images/298.png) 33 | 34 | From the menu that appears on the left, select "Security & permissions." 35 | 36 | ![Manage security](./images/297.png) 37 | 38 | Then "Add or remove." 39 | 40 | ![Manage security continued](./images/296.png) 41 | 42 | Ensure that "Amazon S3" is checked 43 | 44 | ![Manage security S3](./images/295.png) 45 | 46 | Expand the detail link to define specific buckets to grant permissions to. 47 | 48 | ![Select S3 buckets](./images/294.png) 49 | 50 | Add the bucket created by the main deployment stack. You do not need to enable the write permission so leave that box unchecked. 51 | 52 | ![Select S3 the new bucket](./images/293.png) 53 | 54 | ### _Add a data set_ 55 | When you are done, select "Finish" at the bottom of the dialog and navigate to the home screen. From the home screen menu select "Manage data" 56 | 57 | ![Manage data](./images/300.png) 58 | 59 | Click "New data set" 60 | 61 | ![New data set](./images/310.png) 62 | 63 | Select "S3" and create a name for your data source name. In this example we used "AwsCheckVersions." 64 | 65 | ![Select S3](./images/320.png) 66 | 67 | For the next dialog, QuickSight S3 connections require a simple JSON formatted manifest file to indicate the basic metadata of the file object to be imported. You need to create this file manually and you can either make it available as a URL or save it to your local workstation and upload from there. Here is an example of such a file. Note, you should replace the name "version-dashboard-versionss3bucket" with the specific bucket name created by the CloudFormation installation template. That name should appear in the CloudFormation outputs when you executed the stack. Also, we have elected to save it locally so we selected the "Upload" option and clicked the folder icon to browse to the file location: 68 | 69 | ```JSON 70 | { 71 | "fileLocations": [ 72 | { 73 | "URIs": [ 74 | "s3://version-dashboard-versionss3bucket/all_versions.csv" 75 | ] 76 | } 77 | ], 78 | "globalUploadSettings": { 79 | "format": "CSV", 80 | "delimiter": ",", 81 | "containsHeader": "true" 82 | } 83 | } 84 | ``` 85 | Once ready, click "Connect" 86 | 87 | ![Define the manifest](./images/330.png) 88 | 89 | Once the connection is established, you will see a confirmation dialog similar to the following. Click on "Visualize." 90 | 91 | ![Data set created](./images/340.png) 92 | 93 | You are now ready to proceed to the next part of this guide where you will create the dashboards. However, we recommend you set up a schedule to refresh the data from S3 on a regular basis. See below. 94 | 95 | ![Ready to create dashboards](./images/350.png) 96 | 97 | ### _Set up automatic data refresh (optional)_ 98 | The main solution stack will update the version data file in your S3 bucket according to the schedule you set for it. However, QuickSight by default only imports the existing data at the time the data set was created. It does not have awareness of changes to that underlying file in S3. To be sure you are viewing the most recent data, you need to invoke a refresh operation. A refresh can be done manually or automatically on a schedule of your choosing. We recommend setting a schedule that aligns with the cron schedule for your main solution but at least 1 hour after it runs to be sure that it completes its update. 99 | 100 | Begin by selecting "Manage data" from the home page and select the tile for your newly created data set. 101 | 102 | ![Ready to create dashboards](./images/360.png) 103 | 104 | Open the "Schedule refresh" dialog to enter a schedule (note you can refresh you data on-demand from this screen as well). 105 | 106 | ![Ready to create dashboards](./images/370.png) 107 | 108 | Click "Create" to reveal the schedule options dialog. 109 | 110 | ![Ready to create dashboards](./images/380.png) 111 | 112 | Set your schedule to your suit your needs and click "Create" to establish it. 113 | 114 | Step 2: Create the Dashboards 115 | -------- 116 | 117 | After creating the necessary Data Set from the previous section, you can now build an analysis from it. If you created the initial analysis by clicking "Visualize" at the end of the Data Set creation an import process, you can skip these first steps. 118 | 119 | ![New analysis](./images/01.png) 120 | 121 | Select the Data Set that was created by the CloudFormation script. In this example, "aws-check-versions-dataset" refers to the appropriate data. 122 | 123 | ![Data Set](./images/20.png) 124 | 125 | A dialog to create the analysis from the Data Set appears. Select "Create analysis." 126 | 127 | ![Create analysis](./images/30.png) 128 | 129 | You should then see your blank project canvas to begin building visualizations on one or more tabs. 130 | 131 | ![Blank canvas](./images/40.png) 132 | 133 | The tabs and visualizations always start with default names so go ahead and rename them as needed. In this case, this first tab is renamed from "Sheet 1" to "Summary View," which represents the high-level, cross-service quick view we are creating first. 134 | 135 | ![Tab rename](./images/50.png) 136 | 137 | To begin building a visualization, select the type you want from the Visual types options pane (lower left). This example builds a stacked bar graph where each bar represents one of the services in the checks and each segment in the bar depicts a scaled representation of the number of instances of a service that fall into each Generic Determination category. 138 | 139 | ![Visual type](./images/60.png) 140 | 141 | Any given visual type will require two or more minimum data dimensions and potentially additional optionals in order to render an effective chart. The help text for each type provides guidance. Click and drag the appropriate fields for this example as shown in the diagram. They include X axis = Check, Value = Generic Determination, and Group/Color = Generic Determination. The graph should resemble the following. You can also resize the chart according to you needs. You may need to enlarge the size to ensure all textual data is effectively displayed and not automatically obfuscated if QuickSight detects there may be crowding or overlap. 142 | 143 | ![Build bar graph](./images/70.png) 144 | 145 | Note that QuickSight applies its own default color scheme. You may wish to manually override that scheme for a more effective visual. In this example, we use a common green, yellow, orange, and red scheme to indicate increasing potential risk based on how out of date some deployed service versions are. The default here depictied "Significantly Backdated" as green, which is not particularly efffective and has some irony to it. You may click on a green bar segment and reassign it to a more appropriate red color, for example, to represent high potential risk. Note, the colors you select should also be appropriate to any accessability requirements for your audience. 146 | 147 | ![Change color](./images/80.png) 148 | 149 | Here is the final color rendering after applying our scheme to each category. 150 | 151 | ![Final colors](./images/90.png) 152 | 153 | Next we change the title of the graph to something more direct: 154 | 155 | ![Rename](./images/100.png) 156 | 157 | To enhance the Summary tab, we create "Donut" charts to view the disposition of each service at a glance. This is optional. Begin by selecting "+ Add" and "Add visual." 158 | 159 | ![Add new donut](./images/110.png) 160 | 161 | A blank space is added below the summary graph you already created. Select the Donut type option. 162 | 163 | ![Select donut](./images/120.png) 164 | 165 | In the Field wells toward the top you see that the Determination field is used. 166 | 167 | ![Select donut fields](./images/130.png) 168 | 169 | At this point the graph include all services in the checks. QuickSight provides a filter mechanism to narrow down to a single check type. In our example we use RDS. With the graph active, select the filter menu on the left. Then select the + sign to add a new filter and select "Check." 170 | 171 | ![Filter donut](./images/140.png) 172 | 173 | Click on the new filter that now shows: "Check" and "Include - all." We need to select the specific value of rds, rather than including all possible check values. 174 | 175 | ![Filter donut cont.](./images/150.png) 176 | 177 | Click the Apply button and your graph will represent only RDS instances in the checks. Note, that same initial default color scheme will be used. Follow the steps as above to change the color representations, if desired. You may also change the chart title to a more meaningful name. Upon doing so you should see something like the following. 178 | 179 | ![Final donut](./images/160.png) 180 | 181 | You can follow the pattern to create donut charts for each of the checked services. In our example we also modified graph display options to show labels and hide the legend. Make any such adjustments to meet your own needs. Your dashboard might then look like this: 182 | 183 | ![Full summary dash](./images/170.png) 184 | 185 | The next task is to create individual reporting pages/tabs for each service. In our example we created a tab for each service to look like the following: 186 | 187 | ![All tabs](./images/180.png) 188 | 189 | To demonstrate the build for each tab, we will we use RDS as a repeatable an example. RDS is more complicated than other services because there are many different database engine options and each has its own version numbering scheme. Much of that complexity is managed for you by the core functional deployment of this solution, which standardizes the results to fit into the schema used for reporting on all services. 190 | 191 | For the RDS dashboard we recreate the donut graph on the Summary page and further add a new stacked bar graph to differentiate between the database engines as well as a detail matrix to reveal each and every instance identified by the core solution reporting. For additional value, we use QuickSight Actions to enable an interactive experience with dynamic filtering. So, for example, clicking on MySQL in the new bar graph will filter the matrix to show only the MySQL instances. Clicking the disposition in the donut chart can further refine the matrix to show only significantly backdated MySQL instances. 192 | 193 | To begin, you can create a copy of the RDS donut graph from the summary tab and place it on the RDS tab. 194 | 195 | ![Donut replication ... yum](./images/190.png) 196 | 197 | On the RDS tab, add a new visual and select a stacked bar chart. Set the options in the Field wells as X axis = Sub Type, Value = Determination (Count), and Group/Color = Determination. Set your color scheme again, if desired. Then add a filter to the RDS Check field, as you did on the donut graph. It should resemble this: 198 | 199 | ![RDS bars](./images/200.png) 200 | 201 | Next, create a pivot table chart on the same tab by adding a new visual as you did before. 202 | 203 | ![RDS pivot table](./images/210.png) 204 | 205 | Populate the Field wells per the following: 206 | 207 | ![RDS pivot fields](./images/220.png) 208 | 209 | Add a Filter on the Check field, setting it to "rds," as before. In this case we renamed the title to "RDS Resources." The resulting matrix will look like this. 210 | 211 | _Note that the Resource IDs reflect fake names for this example. In your case the specific resources in your account will be listed._ 212 | 213 | ![RDS pivot done](./images/230.png) 214 | 215 | The pivot table matrix by default will show all of your resources across all RDS engine types, which can be quite long and harder to sift through. You can implement Actions in QuickSight to add a dynamic filter to better pinpoint groups of instances. Begin by selecting the bar chart you created and expanding the ellispis on the context menu to reveal the "Actions" menu. 216 | 217 | ![Actions](./images/240.png) 218 | 219 | Click the + or the "Define a custom action" button. 220 | 221 | ![Add action](./images/250.png) 222 | 223 | In the New action dialog, set the Filter scope to "Selected fields" and make sure the values "Sub Type" (i.e. RDS engine type) and "Determination" are checked. You can target both of the other visuals on the page if you want but here we target just the matix "RDS resources." Check the appropriate Target visuals box(es) for your needs. The configuration should look like this: 224 | 225 | ![Edit action](./images/260.png) 226 | 227 | Click the Save button at the bottom. Now when you click on one of the bar segments from the stacked bar graph, the matrix will be automatically filtered to show the reources within that category. Here we selected the heavily backdated mysql instances in the bar chart and the matrix now shows the relevant instances. 228 | 229 | ![Action mysql](./images/270.png) 230 | 231 | You can add a similar action to the donut chart to also filter the matrix. In that case you can only scope the filter on the determination as the "Sub Type" field is not used in that graph, only the Determination is. 232 | 233 | You can repeat the steps above for each service check page. Note that RDS and ElastiCache are the only current checks that have a relevant Sub Type. For RDS it is the engine type and for ElastiCache it is Redis or Memcached. So the stacked bar graph created above for RDS is only useful for ElastiCache. You can simply use the donut graph and a detail matrix. Apply any dynamic filter actions as you need them. 234 | -------------------------------------------------------------------------------- /docs/images/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/01.png -------------------------------------------------------------------------------- /docs/images/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/100.png -------------------------------------------------------------------------------- /docs/images/110.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/110.png -------------------------------------------------------------------------------- /docs/images/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/120.png -------------------------------------------------------------------------------- /docs/images/130.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/130.png -------------------------------------------------------------------------------- /docs/images/140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/140.png -------------------------------------------------------------------------------- /docs/images/150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/150.png -------------------------------------------------------------------------------- /docs/images/160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/160.png -------------------------------------------------------------------------------- /docs/images/170.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/170.png -------------------------------------------------------------------------------- /docs/images/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/180.png -------------------------------------------------------------------------------- /docs/images/190.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/190.png -------------------------------------------------------------------------------- /docs/images/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/20.png -------------------------------------------------------------------------------- /docs/images/200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/200.png -------------------------------------------------------------------------------- /docs/images/210.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/210.png -------------------------------------------------------------------------------- /docs/images/220.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/220.png -------------------------------------------------------------------------------- /docs/images/230.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/230.png -------------------------------------------------------------------------------- /docs/images/240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/240.png -------------------------------------------------------------------------------- /docs/images/250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/250.png -------------------------------------------------------------------------------- /docs/images/260.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/260.png -------------------------------------------------------------------------------- /docs/images/270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/270.png -------------------------------------------------------------------------------- /docs/images/292.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/292.png -------------------------------------------------------------------------------- /docs/images/293.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/293.png -------------------------------------------------------------------------------- /docs/images/294.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/294.png -------------------------------------------------------------------------------- /docs/images/295.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/295.png -------------------------------------------------------------------------------- /docs/images/296.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/296.png -------------------------------------------------------------------------------- /docs/images/297.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/297.png -------------------------------------------------------------------------------- /docs/images/298.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/298.png -------------------------------------------------------------------------------- /docs/images/299.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/299.png -------------------------------------------------------------------------------- /docs/images/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/30.png -------------------------------------------------------------------------------- /docs/images/300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/300.png -------------------------------------------------------------------------------- /docs/images/310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/310.png -------------------------------------------------------------------------------- /docs/images/320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/320.png -------------------------------------------------------------------------------- /docs/images/330.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/330.png -------------------------------------------------------------------------------- /docs/images/340.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/340.png -------------------------------------------------------------------------------- /docs/images/350.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/350.png -------------------------------------------------------------------------------- /docs/images/360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/360.png -------------------------------------------------------------------------------- /docs/images/370.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/370.png -------------------------------------------------------------------------------- /docs/images/380.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/380.png -------------------------------------------------------------------------------- /docs/images/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/40.png -------------------------------------------------------------------------------- /docs/images/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/50.png -------------------------------------------------------------------------------- /docs/images/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/60.png -------------------------------------------------------------------------------- /docs/images/70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/70.png -------------------------------------------------------------------------------- /docs/images/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/80.png -------------------------------------------------------------------------------- /docs/images/90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/docs/images/90.png -------------------------------------------------------------------------------- /events/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{\"message\": \"hello world\"}", 3 | "resource": "/{proxy+}", 4 | "path": "/path/to/resource", 5 | "httpMethod": "POST", 6 | "isBase64Encoded": false, 7 | "queryStringParameters": { 8 | "foo": "bar" 9 | }, 10 | "pathParameters": { 11 | "proxy": "/path/to/resource" 12 | }, 13 | "stageVariables": { 14 | "baz": "qux" 15 | }, 16 | "headers": { 17 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 18 | "Accept-Encoding": "gzip, deflate, sdch", 19 | "Accept-Language": "en-US,en;q=0.8", 20 | "Cache-Control": "max-age=0", 21 | "CloudFront-Forwarded-Proto": "https", 22 | "CloudFront-Is-Desktop-Viewer": "true", 23 | "CloudFront-Is-Mobile-Viewer": "false", 24 | "CloudFront-Is-SmartTV-Viewer": "false", 25 | "CloudFront-Is-Tablet-Viewer": "false", 26 | "CloudFront-Viewer-Country": "US", 27 | "Host": "1234567890.execute-api.us-east-1.amazonaws.com", 28 | "Upgrade-Insecure-Requests": "1", 29 | "User-Agent": "Custom User Agent String", 30 | "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", 31 | "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", 32 | "X-Forwarded-For": "127.0.0.1, 127.0.0.2", 33 | "X-Forwarded-Port": "443", 34 | "X-Forwarded-Proto": "https" 35 | }, 36 | "requestContext": { 37 | "accountId": "123456789012", 38 | "resourceId": "123456", 39 | "stage": "prod", 40 | "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", 41 | "requestTime": "09/Apr/2015:12:34:56 +0000", 42 | "requestTimeEpoch": 1428582896000, 43 | "identity": { 44 | "cognitoIdentityPoolId": null, 45 | "accountId": null, 46 | "cognitoIdentityId": null, 47 | "caller": null, 48 | "accessKey": null, 49 | "sourceIp": "127.0.0.1", 50 | "cognitoAuthenticationType": null, 51 | "cognitoAuthenticationProvider": null, 52 | "userArn": null, 53 | "userAgent": "Custom User Agent String", 54 | "user": null 55 | }, 56 | "path": "/prod/path/to/resource", 57 | "resourcePath": "/{proxy+}", 58 | "httpMethod": "POST", 59 | "apiId": "1234567890", 60 | "protocol": "HTTP/1.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /invoke_version_step/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/invoke_version_step/__init__.py -------------------------------------------------------------------------------- /invoke_version_step/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import random, string 4 | import urllib3 5 | import json 6 | from botocore.exceptions import ClientError 7 | 8 | step = boto3.client('stepfunctions') 9 | http = urllib3.PoolManager() 10 | 11 | def lambda_handler(event, context): 12 | 13 | print(event) 14 | execution_id = randomid(10) 15 | 16 | response_data = {} 17 | 18 | try: 19 | step_response = step.start_execution( 20 | stateMachineArn=os.environ['StepFunctionArn'], 21 | name=execution_id, 22 | input='{}' 23 | ) 24 | 25 | except ClientError as e: 26 | response_data['Data'] = e 27 | send(event, context, 'FAILED', response_data, 'TriggerStepFunction') 28 | else: 29 | response_data['Data'] = step_response['executionArn'] 30 | send(event, context, 'SUCCESS', response_data, 'TriggerStepFunction') 31 | 32 | return 33 | 34 | def randomid(length): 35 | letters = string.ascii_lowercase 36 | return ''.join(random.choice(letters) for i in range(length)) 37 | 38 | def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False): 39 | responseUrl = event['ResponseURL'] 40 | 41 | print(responseUrl) 42 | 43 | responseBody = {} 44 | responseBody['Status'] = responseStatus 45 | responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name 46 | responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name 47 | responseBody['StackId'] = event['StackId'] 48 | responseBody['RequestId'] = event['RequestId'] 49 | responseBody['LogicalResourceId'] = event['LogicalResourceId'] 50 | responseBody['NoEcho'] = noEcho 51 | responseBody['Data'] = responseData 52 | 53 | print(responseBody) 54 | json_responseBody = json.dumps(responseBody) 55 | 56 | print("Response body:\n" + json_responseBody) 57 | 58 | headers = { 59 | 'content-type': '', 60 | 'content-length': str(len(json_responseBody)) 61 | } 62 | 63 | try: 64 | response = http.request('PUT', 65 | responseUrl, 66 | body=json_responseBody, 67 | headers=headers) 68 | print("Status code: " + response.reason) 69 | except Exception as e: 70 | print("send(..) failed executing requests.put(..): " + str(e)) -------------------------------------------------------------------------------- /invoke_version_step/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/invoke_version_step/requirements.txt -------------------------------------------------------------------------------- /stackset-role-template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: > 3 | aws-check-versions 4 | 5 | SAM Template for aws-check-versions 6 | 7 | # Need to update StackSet to pull source Account ID, not child Account IDs 8 | Parameters: 9 | PrimaryAccountId: 10 | Type: String 11 | Default: "111111111111" 12 | Description: Enter Current Account ID 13 | 14 | Resources: 15 | RDSLambdaExecutionRole: 16 | Type: AWS::IAM::Role 17 | Properties: 18 | RoleName: aws-ver-dash-lambda-rds 19 | AssumeRolePolicyDocument: 20 | Statement: 21 | - Effect: Allow 22 | Action: 23 | - sts:AssumeRole 24 | Principal: 25 | AWS: 26 | - !Sub arn:aws:iam::${PrimaryAccountId}:role/aws-ver-dash-lambda-rds 27 | Version: 2012-10-17 28 | Policies: 29 | - PolicyDocument: 30 | Version: 2012-10-17 31 | Statement: 32 | - Effect: Allow 33 | Action: 34 | - rds:DescribeDBInstances 35 | - rds:DescribeDBEngineVersions 36 | Resource: '*' 37 | PolicyName: LambdaRDSDescribe 38 | - PolicyDocument: 39 | Version: 2012-10-17 40 | Statement: 41 | - Effect: Allow 42 | Action: 43 | - logs:CreateLogGroup 44 | - logs:CreateLogStream 45 | - logs:PutLogEvents 46 | Resource: '*' 47 | PolicyName: LambdaCloudwatchWrite 48 | 49 | ESLambdaExecutionRole: 50 | Type: AWS::IAM::Role 51 | Properties: 52 | RoleName: aws-ver-dash-lambda-es 53 | AssumeRolePolicyDocument: 54 | Statement: 55 | - Effect: Allow 56 | Action: 57 | - sts:AssumeRole 58 | Principal: 59 | AWS: 60 | - !Sub arn:aws:iam::${PrimaryAccountId}:role/aws-ver-dash-lambda-es 61 | Version: 2012-10-17 62 | Policies: 63 | - PolicyDocument: 64 | Version: 2012-10-17 65 | Statement: 66 | - Effect: Allow 67 | Action: 68 | - es:ListDomainNames 69 | - es:DescribeElasticSearchDomain 70 | - es:ListElasticsearchVersions 71 | Resource: '*' 72 | PolicyName: LambdaESDescribe 73 | - PolicyDocument: 74 | Version: 2012-10-17 75 | Statement: 76 | - Effect: Allow 77 | Action: 78 | - logs:CreateLogGroup 79 | - logs:CreateLogStream 80 | - logs:PutLogEvents 81 | Resource: '*' 82 | PolicyName: LambdaCloudwatchWrite 83 | 84 | ECLambdaExecutionRole: 85 | Type: AWS::IAM::Role 86 | Properties: 87 | RoleName: aws-ver-dash-lambda-ec 88 | AssumeRolePolicyDocument: 89 | Statement: 90 | - Effect: Allow 91 | Action: 92 | - sts:AssumeRole 93 | Principal: 94 | AWS: 95 | - !Sub arn:aws:iam::${PrimaryAccountId}:role/aws-ver-dash-lambda-ec 96 | Version: 2012-10-17 97 | Policies: 98 | - PolicyDocument: 99 | Version: 2012-10-17 100 | Statement: 101 | - Effect: Allow 102 | Action: 103 | - elasticache:DescribeCacheClusters 104 | - elasticache:DescribeCacheEngineVersions 105 | Resource: '*' 106 | PolicyName: LambdaECDescribe 107 | - PolicyDocument: 108 | Version: 2012-10-17 109 | Statement: 110 | - Effect: Allow 111 | Action: 112 | - logs:CreateLogGroup 113 | - logs:CreateLogStream 114 | - logs:PutLogEvents 115 | Resource: '*' 116 | PolicyName: LambdaCloudwatchWrite 117 | 118 | MSKLambdaExecutionRole: 119 | Type: AWS::IAM::Role 120 | Properties: 121 | RoleName: aws-ver-dash-lambda-msk 122 | AssumeRolePolicyDocument: 123 | Statement: 124 | - Effect: Allow 125 | Action: 126 | - sts:AssumeRole 127 | Principal: 128 | AWS: 129 | - !Sub arn:aws:iam::${PrimaryAccountId}:role/aws-ver-dash-lambda-msk 130 | Version: 2012-10-17 131 | Policies: 132 | - PolicyDocument: 133 | Version: 2012-10-17 134 | Statement: 135 | - Effect: Allow 136 | Action: 137 | - kafka:ListClusters 138 | - kafka:ListConfigurations 139 | - kafka:ListKafkaVersions 140 | Resource: '*' 141 | PolicyName: LambdaMSKDescribe 142 | - PolicyDocument: 143 | Version: 2012-10-17 144 | Statement: 145 | - Effect: Allow 146 | Action: 147 | - logs:CreateLogGroup 148 | - logs:CreateLogStream 149 | - logs:PutLogEvents 150 | Resource: '*' 151 | PolicyName: LambdaCloudwatchWrite 152 | 153 | MQLambdaExecutionRole: 154 | Type: AWS::IAM::Role 155 | Properties: 156 | RoleName: aws-ver-dash-lambda-mq 157 | AssumeRolePolicyDocument: 158 | Statement: 159 | - Effect: Allow 160 | Action: 161 | - sts:AssumeRole 162 | Principal: 163 | AWS: 164 | - !Sub arn:aws:iam::${PrimaryAccountId}:role/aws-ver-dash-lambda-mq 165 | Version: 2012-10-17 166 | Policies: 167 | - PolicyDocument: 168 | Version: 2012-10-17 169 | Statement: 170 | - Effect: Allow 171 | Action: 172 | - mq:ListBrokers 173 | - mq:ListConfigurations 174 | - mq:DescribeBroker 175 | - mq:DescribeBrokerEngineTypes 176 | Resource: '*' 177 | PolicyName: LambdaMQDescribe 178 | - PolicyDocument: 179 | Version: 2012-10-17 180 | Statement: 181 | - Effect: Allow 182 | Action: 183 | - logs:CreateLogGroup 184 | - logs:CreateLogStream 185 | - logs:PutLogEvents 186 | Resource: '*' 187 | PolicyName: LambdaCloudwatchWrite 188 | 189 | EKSLambdaExecutionRole: 190 | Type: AWS::IAM::Role 191 | Properties: 192 | RoleName: aws-ver-dash-lambda-eks 193 | AssumeRolePolicyDocument: 194 | Statement: 195 | - Effect: Allow 196 | Action: 197 | - sts:AssumeRole 198 | Principal: 199 | AWS: 200 | - !Sub arn:aws:iam::${PrimaryAccountId}:role/aws-ver-dash-lambda-eks 201 | Version: 2012-10-17 202 | Policies: 203 | - PolicyDocument: 204 | Version: 2012-10-17 205 | Statement: 206 | - Effect: Allow 207 | Action: 208 | - eks:DescribeCluster 209 | - eks:ListClusters 210 | Resource: '*' 211 | PolicyName: LambdaEKSDescribe 212 | - PolicyDocument: 213 | Version: 2012-10-17 214 | Statement: 215 | - Effect: Allow 216 | Action: 217 | - logs:CreateLogGroup 218 | - logs:CreateLogStream 219 | - logs:PutLogEvents 220 | Resource: '*' 221 | PolicyName: LambdaCloudwatchWrite -------------------------------------------------------------------------------- /template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | aws-check-versions 5 | 6 | SAM Template for aws-check-versions 7 | 8 | # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst 9 | Globals: 10 | Function: 11 | Timeout: 60 12 | 13 | Parameters: 14 | SpecifyAccountsOrEnterAll: 15 | Type: String 16 | Default: '111111111111,222222222222' 17 | Description: Enter an Account ID, a comma delimited list of accounts, enter ALL for an entire AWS Organization, or replace the accounts.csv file with a list of comma delimited accounts 18 | 19 | 20 | Resources: 21 | RDSLambdaExecutionRole: 22 | Type: AWS::IAM::Role 23 | Properties: 24 | RoleName: aws-ver-dash-lambda-rds 25 | AssumeRolePolicyDocument: 26 | Statement: 27 | - Effect: Allow 28 | Action: 29 | - sts:AssumeRole 30 | Principal: 31 | Service: 32 | - lambda.amazonaws.com 33 | Version: 2012-10-17 34 | Policies: 35 | - PolicyDocument: 36 | Version: 2012-10-17 37 | Statement: 38 | - Effect: Allow 39 | Action: 40 | - rds:DescribeDBInstances 41 | - rds:DescribeDBEngineVersions 42 | Resource: '*' 43 | PolicyName: LambdaRDSDescribe 44 | - PolicyDocument: 45 | Version: 2012-10-17 46 | Statement: 47 | - Effect: Allow 48 | Action: 49 | - logs:CreateLogGroup 50 | - logs:CreateLogStream 51 | - logs:PutLogEvents 52 | Resource: '*' 53 | PolicyName: LambdaCloudwatchWrite 54 | - PolicyDocument: 55 | Version: 2012-10-17 56 | Statement: 57 | - Effect: Allow 58 | Action: 59 | - sts:AssumeRole 60 | Resource: 'arn:aws:iam::*:role/aws-ver-dash-lambda-rds' 61 | PolicyName: RDSAssumeRole 62 | 63 | ESLambdaExecutionRole: 64 | Type: AWS::IAM::Role 65 | Properties: 66 | RoleName: aws-ver-dash-lambda-es 67 | AssumeRolePolicyDocument: 68 | Statement: 69 | - Effect: Allow 70 | Action: 71 | - sts:AssumeRole 72 | Principal: 73 | Service: 74 | - lambda.amazonaws.com 75 | Version: 2012-10-17 76 | Policies: 77 | - PolicyDocument: 78 | Version: 2012-10-17 79 | Statement: 80 | - Effect: Allow 81 | Action: 82 | - es:ListDomainNames 83 | - es:DescribeElasticSearchDomain 84 | - es:ListElasticsearchVersions 85 | Resource: '*' 86 | PolicyName: LambdaESDescribe 87 | - PolicyDocument: 88 | Version: 2012-10-17 89 | Statement: 90 | - Effect: Allow 91 | Action: 92 | - logs:CreateLogGroup 93 | - logs:CreateLogStream 94 | - logs:PutLogEvents 95 | Resource: '*' 96 | PolicyName: LambdaCloudwatchWrite 97 | - PolicyDocument: 98 | Version: 2012-10-17 99 | Statement: 100 | - Effect: Allow 101 | Action: 102 | - sts:AssumeRole 103 | Resource: 'arn:aws:iam::*:role/aws-ver-dash-lambda-es' 104 | PolicyName: ESAssumeRole 105 | 106 | ECLambdaExecutionRole: 107 | Type: AWS::IAM::Role 108 | Properties: 109 | RoleName: aws-ver-dash-lambda-ec 110 | AssumeRolePolicyDocument: 111 | Statement: 112 | - Effect: Allow 113 | Action: 114 | - sts:AssumeRole 115 | Principal: 116 | Service: 117 | - lambda.amazonaws.com 118 | Version: 2012-10-17 119 | Policies: 120 | - PolicyDocument: 121 | Version: 2012-10-17 122 | Statement: 123 | - Effect: Allow 124 | Action: 125 | - elasticache:DescribeCacheClusters 126 | - elasticache:DescribeCacheEngineVersions 127 | Resource: '*' 128 | PolicyName: LambdaECDescribe 129 | - PolicyDocument: 130 | Version: 2012-10-17 131 | Statement: 132 | - Effect: Allow 133 | Action: 134 | - logs:CreateLogGroup 135 | - logs:CreateLogStream 136 | - logs:PutLogEvents 137 | Resource: '*' 138 | PolicyName: LambdaCloudwatchWrite 139 | - PolicyDocument: 140 | Version: 2012-10-17 141 | Statement: 142 | - Effect: Allow 143 | Action: 144 | - sts:AssumeRole 145 | Resource: 'arn:aws:iam::*:role/aws-ver-dash-lambda-ec' 146 | PolicyName: ECAssumeRole 147 | 148 | MSKLambdaExecutionRole: 149 | Type: AWS::IAM::Role 150 | Properties: 151 | RoleName: aws-ver-dash-lambda-msk 152 | AssumeRolePolicyDocument: 153 | Statement: 154 | - Effect: Allow 155 | Action: 156 | - sts:AssumeRole 157 | Principal: 158 | Service: 159 | - lambda.amazonaws.com 160 | Version: 2012-10-17 161 | Policies: 162 | - PolicyDocument: 163 | Version: 2012-10-17 164 | Statement: 165 | - Effect: Allow 166 | Action: 167 | - kafka:ListClusters 168 | - kafka:ListConfigurations 169 | - kafka:ListKafkaVersions 170 | Resource: '*' 171 | PolicyName: LambdaMSKDescribe 172 | - PolicyDocument: 173 | Version: 2012-10-17 174 | Statement: 175 | - Effect: Allow 176 | Action: 177 | - logs:CreateLogGroup 178 | - logs:CreateLogStream 179 | - logs:PutLogEvents 180 | Resource: '*' 181 | PolicyName: LambdaCloudwatchWrite 182 | - PolicyDocument: 183 | Version: 2012-10-17 184 | Statement: 185 | - Effect: Allow 186 | Action: 187 | - sts:AssumeRole 188 | Resource: 'arn:aws:iam::*:role/aws-ver-dash-lambda-msk' 189 | PolicyName: MSKAssumeRole 190 | 191 | MQLambdaExecutionRole: 192 | Type: AWS::IAM::Role 193 | Properties: 194 | RoleName: aws-ver-dash-lambda-mq 195 | AssumeRolePolicyDocument: 196 | Statement: 197 | - Effect: Allow 198 | Action: 199 | - sts:AssumeRole 200 | Principal: 201 | Service: 202 | - lambda.amazonaws.com 203 | Version: 2012-10-17 204 | Policies: 205 | - PolicyDocument: 206 | Version: 2012-10-17 207 | Statement: 208 | - Effect: Allow 209 | Action: 210 | - mq:ListBrokers 211 | - mq:ListConfigurations 212 | - mq:DescribeBroker 213 | - mq:DescribeBrokerEngineTypes 214 | Resource: '*' 215 | PolicyName: LambdaMQDescribe 216 | - PolicyDocument: 217 | Version: 2012-10-17 218 | Statement: 219 | - Effect: Allow 220 | Action: 221 | - logs:CreateLogGroup 222 | - logs:CreateLogStream 223 | - logs:PutLogEvents 224 | Resource: '*' 225 | PolicyName: LambdaCloudwatchWrite 226 | - PolicyDocument: 227 | Version: 2012-10-17 228 | Statement: 229 | - Effect: Allow 230 | Action: 231 | - sts:AssumeRole 232 | Resource: 'arn:aws:iam::*:role/aws-ver-dash-lambda-mq' 233 | PolicyName: MQAssumeRole 234 | 235 | EKSLambdaExecutionRole: 236 | Type: AWS::IAM::Role 237 | Properties: 238 | RoleName: aws-ver-dash-lambda-eks 239 | AssumeRolePolicyDocument: 240 | Statement: 241 | - Effect: Allow 242 | Action: 243 | - sts:AssumeRole 244 | Principal: 245 | Service: 246 | - lambda.amazonaws.com 247 | Version: 2012-10-17 248 | Policies: 249 | - PolicyDocument: 250 | Version: 2012-10-17 251 | Statement: 252 | - Effect: Allow 253 | Action: 254 | - eks:DescribeCluster 255 | - eks:ListClusters 256 | Resource: '*' 257 | PolicyName: LambdaEKSDescribe 258 | - PolicyDocument: 259 | Version: 2012-10-17 260 | Statement: 261 | - Effect: Allow 262 | Action: 263 | - logs:CreateLogGroup 264 | - logs:CreateLogStream 265 | - logs:PutLogEvents 266 | Resource: '*' 267 | PolicyName: LambdaCloudwatchWrite 268 | - PolicyDocument: 269 | Version: 2012-10-17 270 | Statement: 271 | - Effect: Allow 272 | Action: 273 | - sts:AssumeRole 274 | Resource: 'arn:aws:iam::*:role/aws-ver-dash-lambda-eks' 275 | PolicyName: EKSAssumeRole 276 | 277 | CombineLambdaExecutionRole: 278 | Type: AWS::IAM::Role 279 | Properties: 280 | AssumeRolePolicyDocument: 281 | Statement: 282 | - Effect: Allow 283 | Action: 284 | - sts:AssumeRole 285 | Principal: 286 | Service: 287 | - lambda.amazonaws.com 288 | Version: 2012-10-17 289 | Policies: 290 | - PolicyDocument: 291 | Version: 2012-10-17 292 | Statement: 293 | - Effect: Allow 294 | Action: 295 | - s3:PutObject 296 | Resource: !Join [ '/', [!GetAtt VersionsS3Bucket.Arn, '*']] 297 | PolicyName: LambdaS3PutPolicy 298 | - PolicyDocument: 299 | Version: 2012-10-17 300 | Statement: 301 | - Effect: Allow 302 | Action: 303 | - logs:CreateLogGroup 304 | - logs:CreateLogStream 305 | - logs:PutLogEvents 306 | Resource: '*' 307 | PolicyName: LambdaCloudwatchWrite 308 | 309 | VersionAcctListExecutionRole: 310 | Type: AWS::IAM::Role 311 | Properties: 312 | AssumeRolePolicyDocument: 313 | Statement: 314 | - Effect: Allow 315 | Action: 316 | - sts:AssumeRole 317 | Principal: 318 | Service: 319 | - lambda.amazonaws.com 320 | Version: 2012-10-17 321 | Policies: 322 | - PolicyDocument: 323 | Version: 2012-10-17 324 | Statement: 325 | - Effect: Allow 326 | Action: 327 | - organizations:ListAccounts 328 | - organizations:DescribeOrganization 329 | Resource: '*' 330 | PolicyName: LambdaS3PutPolicy 331 | - PolicyDocument: 332 | Version: 2012-10-17 333 | Statement: 334 | - Effect: Allow 335 | Action: 336 | - logs:CreateLogGroup 337 | - logs:CreateLogStream 338 | - logs:PutLogEvents 339 | Resource: '*' 340 | PolicyName: LambdaCloudwatchWrite 341 | 342 | CombineAcctLambdaExecutionRole: 343 | Type: AWS::IAM::Role 344 | Properties: 345 | AssumeRolePolicyDocument: 346 | Statement: 347 | - Effect: Allow 348 | Action: 349 | - sts:AssumeRole 350 | Principal: 351 | Service: 352 | - lambda.amazonaws.com 353 | Version: 2012-10-17 354 | Policies: 355 | - PolicyDocument: 356 | Version: 2012-10-17 357 | Statement: 358 | - Effect: Allow 359 | Action: 360 | - s3:PutObject 361 | - s3:GetObject 362 | Resource: !Join [ '/', [!GetAtt VersionsS3Bucket.Arn, '*']] 363 | PolicyName: LambdaS3PutPolicy 364 | - PolicyDocument: 365 | Version: 2012-10-17 366 | Statement: 367 | - Effect: Allow 368 | Action: 369 | - s3:ListBucket 370 | Resource: !GetAtt VersionsS3Bucket.Arn 371 | PolicyName: LambdaS3ListPolicy 372 | - PolicyDocument: 373 | Version: 2012-10-17 374 | Statement: 375 | - Effect: Allow 376 | Action: 377 | - logs:CreateLogGroup 378 | - logs:CreateLogStream 379 | - logs:PutLogEvents 380 | Resource: '*' 381 | PolicyName: LambdaCloudwatchWrite 382 | 383 | LambdaInvokeRole: 384 | Type: AWS::IAM::Role 385 | Properties: 386 | AssumeRolePolicyDocument: 387 | Version: 2012-10-17 388 | Statement: 389 | - Effect: "Allow" 390 | Principal: 391 | Service: 392 | - states.amazonaws.com 393 | Action: "sts:AssumeRole" 394 | Policies: 395 | - PolicyName: LambdaInvokePolicy 396 | PolicyDocument: 397 | Version: 2012-10-17 398 | Statement: 399 | - Effect: Allow 400 | Action: 401 | - lambda:InvokeFunction 402 | Resource: 403 | - !GetAtt VersionAcctListFunction.Arn 404 | - !GetAtt VersionRdsFunction.Arn 405 | - !GetAtt VersionEsFunction.Arn 406 | - !GetAtt VersionEcFunction.Arn 407 | - !GetAtt VersionDocDBFunction.Arn 408 | - !GetAtt VersionEKSFunction.Arn 409 | - !GetAtt VersionMSKFunction.Arn 410 | - !GetAtt VersionMQFunction.Arn 411 | - !GetAtt VersionCombineFunction.Arn 412 | - !GetAtt VersionCombineAcctsFunction.Arn 413 | - PolicyName: StepCloudwatchWrite 414 | PolicyDocument: 415 | Version: 2012-10-17 416 | Statement: 417 | - Effect: Allow 418 | Action: 419 | - logs:CreateLogDelivery 420 | - logs:GetLogDelivery 421 | - logs:UpdateLogDelivery 422 | - logs:DeleteLogDelivery 423 | - logs:ListLogDeliveries 424 | - logs:PutResourcePolicy 425 | - logs:DescribeResourcePolicies 426 | - logs:DescribeLogGroups 427 | Resource: '*' 428 | 429 | StepFunctionInvokeRole: 430 | Type: AWS::IAM::Role 431 | Properties: 432 | AssumeRolePolicyDocument: 433 | Version: 2012-10-17 434 | Statement: 435 | - Effect: 'Allow' 436 | Principal: 437 | Service: 438 | - events.amazonaws.com 439 | Action: 'sts:AssumeRole' 440 | Policies: 441 | - PolicyName: StepFunctionInvokePolicy 442 | PolicyDocument: 443 | Version: 2012-10-17 444 | Statement: 445 | - Effect: Allow 446 | Action: 447 | - states:StartExecution 448 | Resource: 449 | - !Ref VersionStateMachine 450 | 451 | LambdaStepInvokeRole: 452 | Type: AWS::IAM::Role 453 | Properties: 454 | AssumeRolePolicyDocument: 455 | Version: 2012-10-17 456 | Statement: 457 | - Effect: 'Allow' 458 | Principal: 459 | Service: 460 | - lambda.amazonaws.com 461 | Action: 'sts:AssumeRole' 462 | Policies: 463 | - PolicyName: LambdaStepFunctionInvokePolicy 464 | PolicyDocument: 465 | Version: 2012-10-17 466 | Statement: 467 | - Effect: Allow 468 | Action: 469 | - states:StartExecution 470 | Resource: 471 | - !Ref VersionStateMachine 472 | - PolicyName: LambdaWriteLogs 473 | PolicyDocument: 474 | Version: 2012-10-17 475 | Statement: 476 | - Effect: Allow 477 | Action: 478 | - logs:CreateLogGroup 479 | - logs:CreateLogStream 480 | - logs:PutLogEvents 481 | Resource: '*' 482 | 483 | ScheduleStepFunctions: 484 | Type: AWS::Events::Rule 485 | Properties: 486 | Description: 'Trigger Weekly for Step Function' 487 | ScheduleExpression: 'cron(0 6 ? * FRI *)' 488 | State: 'ENABLED' 489 | Targets: 490 | - Arn: !Ref VersionStateMachine 491 | Id: 'TargetVersionStateMachine' 492 | RoleArn: !GetAtt StepFunctionInvokeRole.Arn 493 | 494 | VersionsS3Bucket: 495 | Type: AWS::S3::Bucket 496 | Properties: 497 | VersioningConfiguration: 498 | Status: Enabled 499 | BucketEncryption: 500 | ServerSideEncryptionConfiguration: 501 | - ServerSideEncryptionByDefault: 502 | SSEAlgorithm: AES256 503 | PublicAccessBlockConfiguration: 504 | BlockPublicAcls: true 505 | BlockPublicPolicy: true 506 | IgnorePublicAcls: true 507 | RestrictPublicBuckets: true 508 | 509 | VersionStateMachine: 510 | Type: AWS::Serverless::StateMachine 511 | Properties: 512 | Name: version-dashboard-statemachine 513 | Type: EXPRESS 514 | Logging: 515 | Level: ERROR 516 | Destinations: 517 | - CloudWatchLogsLogGroup: 518 | LogGroupArn: !GetAtt StepFunctionLogGroup.Arn 519 | DefinitionUri: version_state_machine/version_state_machine.asl.json 520 | DefinitionSubstitutions: 521 | VersionAcctListFunctionArn: !GetAtt VersionAcctListFunction.Arn 522 | VersionRdsFunctionArn: !GetAtt VersionRdsFunction.Arn 523 | VersionEsFunctionArn: !GetAtt VersionEsFunction.Arn 524 | VersionDocDBFunctionArn: !GetAtt VersionDocDBFunction.Arn 525 | VersionEKSFunctionArn: !GetAtt VersionEKSFunction.Arn 526 | VersionMSKFunctionArn: !GetAtt VersionMSKFunction.Arn 527 | VersionMQFunctionArn: !GetAtt VersionMQFunction.Arn 528 | VersionEcFunctionArn: !GetAtt VersionEcFunction.Arn 529 | VersionCombineFunctionArn: !GetAtt VersionCombineFunction.Arn 530 | VersionCombineAcctsFunctionArn: !GetAtt VersionCombineAcctsFunction.Arn 531 | Role: !GetAtt LambdaInvokeRole.Arn 532 | 533 | StepFunctionLogGroup: 534 | Type: AWS::Logs::LogGroup 535 | 536 | 537 | VersionAcctListFunction: 538 | Type: AWS::Serverless::Function 539 | Properties: 540 | CodeUri: version_acct_list/ 541 | Handler: app.lambda_handler 542 | Runtime: python3.7 543 | Role: !GetAtt VersionAcctListExecutionRole.Arn 544 | Environment: 545 | Variables: 546 | ACCOUNTS: !Ref SpecifyAccountsOrEnterAll 547 | 548 | VersionRdsFunction: 549 | Type: AWS::Serverless::Function 550 | Properties: 551 | CodeUri: version_rds/ 552 | Handler: app.lambda_handler 553 | Runtime: python3.7 554 | Role: !GetAtt RDSLambdaExecutionRole.Arn 555 | 556 | VersionEsFunction: 557 | Type: AWS::Serverless::Function 558 | Properties: 559 | CodeUri: version_es/ 560 | Handler: app.lambda_handler 561 | Runtime: python3.7 562 | Role: !GetAtt ESLambdaExecutionRole.Arn 563 | 564 | VersionEcFunction: 565 | Type: AWS::Serverless::Function 566 | Properties: 567 | CodeUri: version_ec/ 568 | Handler: app.lambda_handler 569 | Runtime: python3.7 570 | Role: !GetAtt ECLambdaExecutionRole.Arn 571 | 572 | VersionDocDBFunction: 573 | Type: AWS::Serverless::Function 574 | Properties: 575 | CodeUri: version_docdb/ 576 | Handler: app.lambda_handler 577 | Runtime: python3.7 578 | Role: !GetAtt RDSLambdaExecutionRole.Arn 579 | 580 | VersionMSKFunction: 581 | Type: AWS::Serverless::Function 582 | Properties: 583 | CodeUri: version_msk/ 584 | Handler: app.lambda_handler 585 | Runtime: python3.7 586 | Role: !GetAtt MSKLambdaExecutionRole.Arn 587 | 588 | VersionMQFunction: 589 | Type: AWS::Serverless::Function 590 | Properties: 591 | CodeUri: version_mq/ 592 | Handler: app.lambda_handler 593 | Runtime: python3.7 594 | Role: !GetAtt MQLambdaExecutionRole.Arn 595 | 596 | VersionEKSFunction: 597 | Type: AWS::Serverless::Function 598 | Properties: 599 | CodeUri: version_eks/ 600 | Handler: app.lambda_handler 601 | Runtime: python3.7 602 | Role: !GetAtt EKSLambdaExecutionRole.Arn 603 | 604 | InvokeVersionStep: 605 | Type: AWS::Serverless::Function 606 | Properties: 607 | CodeUri: invoke_version_step/ 608 | Handler: app.lambda_handler 609 | Runtime: python3.7 610 | Environment: 611 | Variables: 612 | StepFunctionArn: !Ref VersionStateMachine 613 | Role: !GetAtt LambdaStepInvokeRole.Arn 614 | 615 | VersionCombineFunction: 616 | Type: AWS::Serverless::Function 617 | Properties: 618 | CodeUri: version_combine/ 619 | Handler: app.lambda_handler 620 | Runtime: python3.7 621 | Environment: 622 | Variables: 623 | s3BucketName: !Ref VersionsS3Bucket 624 | Role: !GetAtt CombineLambdaExecutionRole.Arn 625 | 626 | VersionCombineAcctsFunction: 627 | Type: AWS::Serverless::Function 628 | Properties: 629 | CodeUri: version_combine_acct/ 630 | Handler: app.lambda_handler 631 | Runtime: python3.7 632 | MemorySize: 1024 633 | Timeout: 300 634 | Environment: 635 | Variables: 636 | s3BucketName: !Ref VersionsS3Bucket 637 | Role: !GetAtt CombineAcctLambdaExecutionRole.Arn 638 | 639 | TriggerStepFunction: 640 | Type: Custom::TriggerStepFunction 641 | Version: '1.0' 642 | Properties: 643 | ServiceToken: !GetAtt InvokeVersionStep.Arn 644 | Region: !Ref 'AWS::Region' 645 | Version: '1.3' 646 | 647 | Outputs: 648 | # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function 649 | # Find out more about other implicit resources you can reference within SAM 650 | # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api 651 | VersionsS3Bucket: 652 | Description: "S3 Bucket for latest version files" 653 | Value: !Ref VersionsS3Bucket 654 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/test_handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import pytest 4 | 5 | from hello_world import app 6 | 7 | 8 | @pytest.fixture() 9 | def apigw_event(): 10 | """ Generates API GW Event""" 11 | 12 | return { 13 | "body": '{ "test": "body"}', 14 | "resource": "/{proxy+}", 15 | "requestContext": { 16 | "resourceId": "123456", 17 | "apiId": "1234567890", 18 | "resourcePath": "/{proxy+}", 19 | "httpMethod": "POST", 20 | "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", 21 | "accountId": "123456789012", 22 | "identity": { 23 | "apiKey": "", 24 | "userArn": "", 25 | "cognitoAuthenticationType": "", 26 | "caller": "", 27 | "userAgent": "Custom User Agent String", 28 | "user": "", 29 | "cognitoIdentityPoolId": "", 30 | "cognitoIdentityId": "", 31 | "cognitoAuthenticationProvider": "", 32 | "sourceIp": "127.0.0.1", 33 | "accountId": "", 34 | }, 35 | "stage": "prod", 36 | }, 37 | "queryStringParameters": {"foo": "bar"}, 38 | "headers": { 39 | "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", 40 | "Accept-Language": "en-US,en;q=0.8", 41 | "CloudFront-Is-Desktop-Viewer": "true", 42 | "CloudFront-Is-SmartTV-Viewer": "false", 43 | "CloudFront-Is-Mobile-Viewer": "false", 44 | "X-Forwarded-For": "127.0.0.1, 127.0.0.2", 45 | "CloudFront-Viewer-Country": "US", 46 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 47 | "Upgrade-Insecure-Requests": "1", 48 | "X-Forwarded-Port": "443", 49 | "Host": "1234567890.execute-api.us-east-1.amazonaws.com", 50 | "X-Forwarded-Proto": "https", 51 | "X-Amz-Cf-Id": "aaaaaaaaaae3VYQb9jd-nvCd-de396Uhbp027Y2JvkCPNLmGJHqlaA==", 52 | "CloudFront-Is-Tablet-Viewer": "false", 53 | "Cache-Control": "max-age=0", 54 | "User-Agent": "Custom User Agent String", 55 | "CloudFront-Forwarded-Proto": "https", 56 | "Accept-Encoding": "gzip, deflate, sdch", 57 | }, 58 | "pathParameters": {"proxy": "/examplepath"}, 59 | "httpMethod": "POST", 60 | "stageVariables": {"baz": "qux"}, 61 | "path": "/examplepath", 62 | } 63 | 64 | 65 | def test_lambda_handler(apigw_event, mocker): 66 | 67 | ret = app.lambda_handler(apigw_event, "") 68 | data = json.loads(ret["body"]) 69 | 70 | assert ret["statusCode"] == 200 71 | assert "message" in ret["body"] 72 | assert data["message"] == "hello world" 73 | # assert "location" in data.dict_keys() 74 | -------------------------------------------------------------------------------- /version_acct_list/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_acct_list/__init__.py -------------------------------------------------------------------------------- /version_acct_list/accounts.csv: -------------------------------------------------------------------------------- 1 | 1.11111E+11 2 | 2.22222E+11 3 | 3.33333E+11 4 | 4.44444E+11 5 | 5.55556E+11 -------------------------------------------------------------------------------- /version_acct_list/app.py: -------------------------------------------------------------------------------- 1 | import json 2 | import boto3 3 | import logging 4 | import os 5 | import csv 6 | 7 | org = boto3.client('organizations') 8 | 9 | def lambda_handler(event, context): 10 | 11 | accts = {} 12 | accts["accounts"] = [] 13 | 14 | print(os.environ['ACCOUNTS']) 15 | 16 | if ',' in os.environ['ACCOUNTS']: 17 | getAccts(accts) 18 | elif '.csv' in os.environ['ACCOUNTS']: 19 | getCSV(accts) 20 | elif 'ALL' in os.environ['ACCOUNTS']: 21 | getOrgList(accts) 22 | else: 23 | getAccts(accts) 24 | 25 | 26 | 27 | return accts 28 | 29 | def getAccts(accts): 30 | accountList = os.environ['ACCOUNTS'] 31 | accountSplit = accountList.split(',') 32 | 33 | for i in accountSplit: 34 | acctslist = accts["accounts"] 35 | acct = {'account' : i} 36 | acctslist.append(acct) 37 | print(acct) 38 | 39 | 40 | 41 | def getCSV(accts): 42 | filePath = os.environ['LAMBDA_TASK_ROOT'] + "/" + os.environ['ACCOUNTS'] 43 | 44 | with open(filePath) as csv_file: 45 | csv_reader = csv.reader(csv_file, delimiter=',') 46 | for row in csv_reader: 47 | print(row) 48 | acctslist = accts["accounts"] 49 | acct = {'account' : row[0]} 50 | acctslist.append(acct) 51 | 52 | 53 | def getOrgList(accts): 54 | response = org.list_accounts() 55 | 56 | for id in response.get('Accounts'): 57 | acctslist = accts["accounts"] 58 | acct = {'account' : id.get('Id')} 59 | acctslist.append(acct) 60 | 61 | -------------------------------------------------------------------------------- /version_acct_list/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_acct_list/requirements.txt -------------------------------------------------------------------------------- /version_combine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_combine/__init__.py -------------------------------------------------------------------------------- /version_combine/app.py: -------------------------------------------------------------------------------- 1 | import json 2 | import boto3 3 | import csv 4 | import os 5 | import logging 6 | 7 | s3 = boto3.client('s3') 8 | 9 | bucket = os.environ['s3BucketName'] 10 | 11 | def lambda_handler(event, context): 12 | 13 | logger = logging.getLogger() 14 | logger.setLevel(logging.INFO) 15 | 16 | logger.info("Processing account %s", event) 17 | 18 | accountid = "" 19 | 20 | for get in event: 21 | for account in get: 22 | if not get[account]['Account'] == '': 23 | accountid = get[account]['Account'] 24 | 25 | with open("/tmp/" + accountid + ".csv", "w+") as csvfile: 26 | tmpcsv = csv.writer(csvfile) 27 | tmpcsv.writerow(["Check", "Account", "Region", "Resource ID", "Sub Type", "Deployed Version", "Latest Version", 'Versions Back']) 28 | 29 | for check in event: 30 | for instance in check: 31 | tmpcsv.writerow([check[instance]['Check'], 32 | check[instance]['Account'], 33 | check[instance]['Region'], 34 | check[instance]['Resource ID'], 35 | check[instance]['Sub Type'], 36 | check[instance]['Deployed Version'], 37 | check[instance]['Latest Version'], 38 | check[instance]['Versions Back'] 39 | ]) 40 | 41 | s3.upload_file('/tmp/' + accountid + '.csv', bucket, accountid + '.csv') 42 | 43 | return { 44 | "statusCode": 200, 45 | "body": json.dumps({ 46 | "message": "version_combine completed", 47 | }), 48 | } 49 | -------------------------------------------------------------------------------- /version_combine/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_combine/requirements.txt -------------------------------------------------------------------------------- /version_combine_acct/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_combine_acct/__init__.py -------------------------------------------------------------------------------- /version_combine_acct/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import boto3 3 | import csv 4 | import os 5 | import json 6 | import logging 7 | 8 | s3 = boto3.client('s3') 9 | 10 | s3bucket = os.environ['s3BucketName'] 11 | 12 | def lambda_handler(event, context): 13 | 14 | logger = logging.getLogger() 15 | logger.setLevel(logging.INFO) 16 | 17 | response = s3.list_objects( 18 | Bucket=s3bucket 19 | ) 20 | 21 | with open("/tmp/all_versions.csv", "w+") as csvfile: 22 | tmpcsv = csv.writer(csvfile) 23 | tmpcsv.writerow(["Check", "Account", "Region", "Resource ID", "Sub Type", "Deployed Version", "Latest Version", 'Versions Back']) 24 | 25 | 26 | 27 | for i in response.get('Contents'): 28 | 29 | if not i.get('Key') == 'all_versions.csv': 30 | response = s3.get_object(Bucket=s3bucket, Key=i.get('Key')) 31 | data = response['Body'].read().decode('utf-8') 32 | 33 | splitlines = data.splitlines() 34 | 35 | for n in splitlines: 36 | splitstr = n.split(',') 37 | if not splitstr[0] == 'Check': 38 | tmpcsv.writerow(splitstr) 39 | 40 | 41 | 42 | 43 | s3.upload_file('/tmp/all_versions.csv', s3bucket, 'all_versions.csv') 44 | 45 | return { 46 | "statusCode": 200, 47 | "body": json.dumps({ 48 | "message": "version_combine completed", 49 | }), 50 | } -------------------------------------------------------------------------------- /version_combine_acct/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_combine_acct/requirements.txt -------------------------------------------------------------------------------- /version_docdb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_docdb/__init__.py -------------------------------------------------------------------------------- /version_docdb/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import logging 3 | import botocore.exceptions 4 | from distutils.version import LooseVersion as versioncmp 5 | 6 | def lambda_handler(event, context): 7 | 8 | logger = logging.getLogger() 9 | logger.setLevel(logging.INFO) 10 | 11 | master_session = boto3.session.Session() 12 | sts = master_session.client('sts') 13 | 14 | current_account = sts.get_caller_identity()['Account'] 15 | 16 | id = event["account"] 17 | regions = boto3.session.Session().get_available_regions('docdb') 18 | 19 | resource_output = dict() 20 | 21 | for region in regions: 22 | 23 | if id != current_account: 24 | role = "arn:aws:iam::{}:role/aws-ver-dash-lambda-rds" 25 | role_arn = role.format(id) 26 | try: 27 | assume_role_response = sts.assume_role( 28 | RoleArn=role_arn, RoleSessionName="LambdaExecution") 29 | 30 | sts_connection = boto3.client('sts') 31 | acct_b = sts_connection.assume_role( 32 | RoleArn=role_arn, 33 | RoleSessionName="cross_acct_lambda" 34 | ) 35 | 36 | ACCESS_KEY = acct_b['Credentials']['AccessKeyId'] 37 | SECRET_KEY = acct_b['Credentials']['SecretAccessKey'] 38 | SESSION_TOKEN = acct_b['Credentials']['SessionToken'] 39 | 40 | # create service client using the assumed role credentials, e.g. S3 41 | docdb = boto3.client( 42 | 'docdb', 43 | aws_access_key_id=ACCESS_KEY, 44 | aws_secret_access_key=SECRET_KEY, 45 | aws_session_token=SESSION_TOKEN, 46 | region_name=region, 47 | ) 48 | except Exception as e: 49 | logger.error("Assume role error %s", e) 50 | 51 | else: 52 | docdb = boto3.client('docdb', region_name=region) 53 | 54 | print(region) 55 | try: 56 | instances = docdb.describe_db_instances() 57 | except botocore.exceptions.ClientError as e: 58 | if e.response['Error']['Code'] == 'AuthFailure': 59 | print('Region {region} seems to be disabled for this account, skipping') 60 | continue 61 | except: 62 | raise e 63 | else: 64 | instances = instances["DBInstances"] 65 | 66 | versions = get_versions(docdb) 67 | versions_len = len(versions) 68 | 69 | for instance in instances: 70 | if instance['Engine'] == 'docdb': 71 | for i in range(versions_len): 72 | if instance['EngineVersion'] == versions[i]: 73 | versions_back = i 74 | resource_output[instance['DBInstanceIdentifier']] = {'Check': 'docdb', 75 | 'Account': id, 76 | 'Region': region, 77 | 'Resource ID': instance['DBInstanceIdentifier'], 78 | 'Sub Type': instance['Engine'], 79 | 'Deployed Version': instance['EngineVersion'], 80 | 'Latest Version': versions[0], 81 | 'Versions Back': versions_back 82 | } 83 | 84 | return resource_output 85 | 86 | def get_versions(docdb): 87 | 88 | docdb_versions = [] 89 | 90 | try: 91 | engine_versions = docdb.describe_db_engine_versions(Engine='docdb') 92 | except botocore.exceptions.ClientError as e: 93 | if e.response['Error']['Code'] == 'InvalidParameterValue': 94 | print('Docdb not supported in this region') 95 | except: 96 | raise e 97 | else: 98 | for versions in engine_versions['DBEngineVersions']: 99 | docdb_versions.append(versions['EngineVersion']) 100 | docdb_versions.sort(key=versioncmp, reverse=True) 101 | 102 | return docdb_versions -------------------------------------------------------------------------------- /version_docdb/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_docdb/requirements.txt -------------------------------------------------------------------------------- /version_ec/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_ec/__init__.py -------------------------------------------------------------------------------- /version_ec/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import logging 3 | import botocore.exceptions 4 | from distutils.version import LooseVersion as versioncmp 5 | 6 | def lambda_handler(event, context): 7 | 8 | logger = logging.getLogger() 9 | logger.setLevel(logging.INFO) 10 | 11 | master_session = boto3.session.Session() 12 | sts = master_session.client('sts') 13 | 14 | current_account = sts.get_caller_identity()['Account'] 15 | 16 | id = event["account"] 17 | regions = boto3.session.Session().get_available_regions('elasticache') 18 | 19 | resource_output = dict() 20 | 21 | for region in regions: 22 | 23 | if id != current_account: 24 | role = "arn:aws:iam::{}:role/aws-ver-dash-lambda-ec" 25 | role_arn = role.format(id) 26 | try: 27 | assume_role_response = sts.assume_role( 28 | RoleArn=role_arn, RoleSessionName="LambdaExecution") 29 | 30 | sts_connection = boto3.client('sts') 31 | acct_b = sts_connection.assume_role( 32 | RoleArn=role_arn, 33 | RoleSessionName="cross_acct_lambda" 34 | ) 35 | 36 | ACCESS_KEY = acct_b['Credentials']['AccessKeyId'] 37 | SECRET_KEY = acct_b['Credentials']['SecretAccessKey'] 38 | SESSION_TOKEN = acct_b['Credentials']['SessionToken'] 39 | 40 | # create service client using the assumed role credentials, e.g. S3 41 | ec = boto3.client( 42 | 'elasticache', 43 | aws_access_key_id=ACCESS_KEY, 44 | aws_secret_access_key=SECRET_KEY, 45 | aws_session_token=SESSION_TOKEN, 46 | region_name=region, 47 | ) 48 | except Exception as e: 49 | logger.error("Assume role error %s", e) 50 | 51 | else: 52 | ec = boto3.client('elasticache', region_name=region) 53 | 54 | try: 55 | clusters = ec.describe_cache_clusters() 56 | except botocore.exceptions.ClientError as e: 57 | if e.response['Error']['Code'] == 'AuthFailure': 58 | print('Region {region} seems to be disabled for this account, skipping') 59 | continue 60 | except: 61 | raise e 62 | else: 63 | memcache_versions = get_versions(ec, 'memcached') 64 | mem_versions_len = len(memcache_versions) 65 | redis_versions = get_versions(ec, 'redis') 66 | red_versions_len = len(redis_versions) 67 | 68 | for cluster in clusters['CacheClusters']: 69 | if cluster['Engine'] == 'memcached': 70 | engine_latest = memcache_versions[0] 71 | for i in range(mem_versions_len): 72 | if memcache_versions[i] == cluster['EngineVersion']: 73 | versions_back = i 74 | elif cluster['Engine'] == 'redis': 75 | engine_latest = redis_versions[0] 76 | for i in range(red_versions_len): 77 | if redis_versions[i] == cluster['EngineVersion']: 78 | versions_back = i 79 | 80 | resource_output[cluster['CacheClusterId']] = {'Check': 'elasticache', 81 | 'Account': id, 82 | 'Region': region, 83 | 'Resource ID': cluster['CacheClusterId'], 84 | 'Sub Type': cluster['Engine'], 85 | 'Deployed Version': cluster['EngineVersion'], 86 | 'Latest Version': engine_latest, 87 | 'Versions Back': versions_back 88 | } 89 | 90 | return resource_output 91 | 92 | def get_versions(ec, engine): 93 | 94 | params = ec.describe_cache_engine_versions(Engine=engine) 95 | versions = [] 96 | 97 | for version in params['CacheEngineVersions']: 98 | versions.append(version['EngineVersion']) 99 | versions.sort(key=versioncmp, reverse=True) 100 | 101 | return versions 102 | -------------------------------------------------------------------------------- /version_ec/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_ec/requirements.txt -------------------------------------------------------------------------------- /version_eks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_eks/__init__.py -------------------------------------------------------------------------------- /version_eks/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import logging 3 | import botocore.exceptions 4 | 5 | def lambda_handler(event, context): 6 | 7 | eks_versions = ['1.29', '1.28', '1.27', '1.26', '1.26', '1.25', '1.24'] 8 | eks_versions_len = len(eks_versions) 9 | 10 | logger = logging.getLogger() 11 | logger.setLevel(logging.INFO) 12 | 13 | master_session = boto3.session.Session() 14 | sts = master_session.client('sts') 15 | 16 | current_account = sts.get_caller_identity()['Account'] 17 | 18 | id = event["account"] 19 | regions = boto3.session.Session().get_available_regions('eks') 20 | 21 | resource_output = dict() 22 | 23 | for region in regions: 24 | if id != current_account: 25 | role = "arn:aws:iam::{}:role/aws-ver-dash-lambda-eks" 26 | role_arn = role.format(id) 27 | try: 28 | assume_role_response = sts.assume_role( 29 | RoleArn=role_arn, RoleSessionName="LambdaExecution") 30 | 31 | sts_connection = boto3.client('sts') 32 | acct_b = sts_connection.assume_role( 33 | RoleArn=role_arn, 34 | RoleSessionName="cross_acct_lambda" 35 | ) 36 | 37 | ACCESS_KEY = acct_b['Credentials']['AccessKeyId'] 38 | SECRET_KEY = acct_b['Credentials']['SecretAccessKey'] 39 | SESSION_TOKEN = acct_b['Credentials']['SessionToken'] 40 | 41 | # create service client using the assumed role credentials, e.g. S3 42 | eks = boto3.client( 43 | 'eks', 44 | aws_access_key_id=ACCESS_KEY, 45 | aws_secret_access_key=SECRET_KEY, 46 | aws_session_token=SESSION_TOKEN, 47 | region_name=region, 48 | ) 49 | except Exception as e: 50 | logger.error("Assume role error %s", e) 51 | 52 | else: 53 | eks = boto3.client('eks', region_name=region) 54 | 55 | try: 56 | clusterslist = eks.list_clusters() 57 | except botocore.exceptions.ClientError as e: 58 | if e.response['Error']['Code'] == 'AuthFailure': 59 | print('Region {region} seems to be disabled for this account, skipping') 60 | continue 61 | except: 62 | raise e 63 | else: 64 | clusterslist = clusterslist['clusters'] 65 | 66 | for clusterName in clusterslist: 67 | instance = eks.describe_cluster(name=clusterName) 68 | for i in range(eks_versions_len): 69 | if instance['cluster']['version'] == eks_versions[i]: 70 | versions_back = i 71 | 72 | resource_output[instance['cluster']['name']] = {'Check': 'eks', 73 | 'Account': id, 74 | 'Region': region, 75 | 'Resource ID': instance['cluster']['name'], 76 | 'Sub Type': '', 77 | 'Deployed Version': instance['cluster']['version'], 78 | 'Latest Version': eks_versions[0], 79 | 'Versions Back': versions_back 80 | } 81 | 82 | return resource_output 83 | -------------------------------------------------------------------------------- /version_eks/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_eks/requirements.txt -------------------------------------------------------------------------------- /version_es/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_es/__init__.py -------------------------------------------------------------------------------- /version_es/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import logging 3 | import botocore.exceptions 4 | 5 | def lambda_handler(event, context): 6 | 7 | logger = logging.getLogger() 8 | logger.setLevel(logging.INFO) 9 | 10 | master_session = boto3.session.Session() 11 | sts = master_session.client('sts') 12 | 13 | current_account = sts.get_caller_identity()['Account'] 14 | 15 | id = event["account"] 16 | regions = boto3.session.Session().get_available_regions('es') 17 | 18 | resource_output = dict() 19 | 20 | for region in regions: 21 | 22 | if id != current_account: 23 | role = "arn:aws:iam::{}:role/aws-ver-dash-lambda-es" 24 | role_arn = role.format(id) 25 | try: 26 | assume_role_response = sts.assume_role( 27 | RoleArn=role_arn, RoleSessionName="LambdaExecution") 28 | 29 | sts_connection = boto3.client('sts') 30 | acct_b = sts_connection.assume_role( 31 | RoleArn=role_arn, 32 | RoleSessionName="cross_acct_lambda" 33 | ) 34 | 35 | ACCESS_KEY = acct_b['Credentials']['AccessKeyId'] 36 | SECRET_KEY = acct_b['Credentials']['SecretAccessKey'] 37 | SESSION_TOKEN = acct_b['Credentials']['SessionToken'] 38 | 39 | # create service client using the assumed role credentials, e.g. S3 40 | es = boto3.client( 41 | 'es', 42 | aws_access_key_id=ACCESS_KEY, 43 | aws_secret_access_key=SECRET_KEY, 44 | aws_session_token=SESSION_TOKEN, 45 | region_name=region, 46 | ) 47 | except Exception as e: 48 | logger.error("Assume role error %s", e) 49 | 50 | else: 51 | es = boto3.client('es', region_name=region) 52 | 53 | try: 54 | domains = es.list_domain_names() 55 | except botocore.exceptions.ClientError as e: 56 | if e.response['Error']['Code'] == 'AuthFailure': 57 | print('Region {region} seems to be disabled for this account, skipping') 58 | continue 59 | except: 60 | raise e 61 | else: 62 | versions = es.list_elasticsearch_versions()['ElasticsearchVersions'] 63 | versions.sort(reverse=True) 64 | length = len(versions) 65 | 66 | for domain in domains['DomainNames']: 67 | domain_describe = es.describe_elasticsearch_domain(DomainName=domain['DomainName']) 68 | deployed_version = domain_describe['DomainStatus']['ElasticsearchVersion'] 69 | for i in range(length): 70 | if versions[i] == deployed_version: 71 | versions_back = i 72 | 73 | resource_output[domain['DomainName']] = {'Check': 'elasticsearch', 74 | 'Account': id, 75 | 'Region': region, 76 | 'Resource ID': domain['DomainName'], 77 | 'Sub Type': '', 78 | 'Deployed Version': domain_describe['DomainStatus']['ElasticsearchVersion'], 79 | 'Latest Version': versions[0], 80 | 'Versions Back': versions_back 81 | } 82 | 83 | return resource_output 84 | -------------------------------------------------------------------------------- /version_es/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_es/requirements.txt -------------------------------------------------------------------------------- /version_mq/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_mq/__init__.py -------------------------------------------------------------------------------- /version_mq/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import logging 3 | import botocore.exceptions 4 | from distutils.version import LooseVersion as versioncmp 5 | 6 | def lambda_handler(event, context): 7 | 8 | logger = logging.getLogger() 9 | logger.setLevel(logging.INFO) 10 | 11 | master_session = boto3.session.Session() 12 | sts = master_session.client('sts') 13 | 14 | current_account = sts.get_caller_identity()['Account'] 15 | 16 | id = event["account"] 17 | regions = boto3.session.Session().get_available_regions('mq') 18 | 19 | resource_output = dict() 20 | 21 | for region in regions: 22 | 23 | if id != current_account: 24 | role = "arn:aws:iam::{}:role/aws-ver-dash-lambda-mq" 25 | role_arn = role.format(id) 26 | try: 27 | assume_role_response = sts.assume_role( 28 | RoleArn=role_arn, RoleSessionName="LambdaExecution") 29 | 30 | sts_connection = boto3.client('sts') 31 | acct_b = sts_connection.assume_role( 32 | RoleArn=role_arn, 33 | RoleSessionName="cross_acct_lambda" 34 | ) 35 | 36 | ACCESS_KEY = acct_b['Credentials']['AccessKeyId'] 37 | SECRET_KEY = acct_b['Credentials']['SecretAccessKey'] 38 | SESSION_TOKEN = acct_b['Credentials']['SessionToken'] 39 | 40 | # create service client using the assumed role credentials, e.g. S3 41 | mq = boto3.client( 42 | 'mq', 43 | aws_access_key_id=ACCESS_KEY, 44 | aws_secret_access_key=SECRET_KEY, 45 | aws_session_token=SESSION_TOKEN, 46 | region_name=region, 47 | ) 48 | except Exception as e: 49 | logger.error("Assume role error %s", e) 50 | 51 | else: 52 | mq = boto3.client('mq', region_name=region) 53 | 54 | try: 55 | brokerslist = mq.list_brokers() 56 | except botocore.exceptions.ClientError as e: 57 | if e.response['Error']['Code'] == 'AuthFailure': 58 | print('Region {region} seems to be disabled for this account, skipping') 59 | continue 60 | except: 61 | raise e 62 | else: 63 | brokers = brokerslist["BrokerSummaries"] 64 | 65 | for broker in brokers: 66 | mq_params = mq.describe_broker(BrokerId=broker['BrokerId']) 67 | enginetype = mq_params['EngineType'] 68 | deploy_version = mq_params['EngineVersion'] 69 | versions = get_versions(mq, enginetype) 70 | versions_len = len(versions) 71 | for i in range(versions_len): 72 | if deploy_version == versions[i]: 73 | versions_back = i 74 | 75 | resource_output['BrokerName'] = {'Check': 'mq', 76 | 'Account': id, 77 | 'Region': region, 78 | 'Resource ID': broker['BrokerName'], 79 | 'Sub Type': enginetype, 80 | 'Deployed Version': deploy_version, 81 | 'Latest Version': versions[0], 82 | 'Versions Back': versions_back 83 | } 84 | 85 | return resource_output 86 | 87 | def get_versions(mq, engine_type): 88 | 89 | engine_versions = mq.describe_broker_engine_types() 90 | mq_versions = [] 91 | 92 | for enginetype in engine_versions["BrokerEngineTypes"]: 93 | if enginetype["EngineType"].casefold() == engine_type.casefold(): 94 | for versions in enginetype["EngineVersions"]: 95 | mq_versions.append(versions['Name']) 96 | mq_versions.sort(key=versioncmp, reverse=True) 97 | return mq_versions 98 | -------------------------------------------------------------------------------- /version_mq/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_mq/requirements.txt -------------------------------------------------------------------------------- /version_msk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_msk/__init__.py -------------------------------------------------------------------------------- /version_msk/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import logging 3 | import botocore.exceptions 4 | from distutils.version import LooseVersion as versioncmp 5 | 6 | def lambda_handler(event, context): 7 | 8 | logger = logging.getLogger() 9 | logger.setLevel(logging.INFO) 10 | 11 | master_session = boto3.session.Session() 12 | sts = master_session.client('sts') 13 | 14 | current_account = sts.get_caller_identity()['Account'] 15 | 16 | id = event["account"] 17 | regions = boto3.session.Session().get_available_regions('kafka') 18 | 19 | resource_output = dict() 20 | 21 | for region in regions: 22 | 23 | if id != current_account: 24 | role = "arn:aws:iam::{}:role/aws-ver-dash-lambda-msk" 25 | role_arn = role.format(id) 26 | try: 27 | assume_role_response = sts.assume_role( 28 | RoleArn=role_arn, RoleSessionName="LambdaExecution") 29 | 30 | sts_connection = boto3.client('sts') 31 | acct_b = sts_connection.assume_role( 32 | RoleArn=role_arn, 33 | RoleSessionName="cross_acct_lambda" 34 | ) 35 | 36 | ACCESS_KEY = acct_b['Credentials']['AccessKeyId'] 37 | SECRET_KEY = acct_b['Credentials']['SecretAccessKey'] 38 | SESSION_TOKEN = acct_b['Credentials']['SessionToken'] 39 | 40 | # create service client using the assumed role credentials, e.g. S3 41 | msk = boto3.client( 42 | 'kafka', 43 | aws_access_key_id=ACCESS_KEY, 44 | aws_secret_access_key=SECRET_KEY, 45 | aws_session_token=SESSION_TOKEN, 46 | region_name=region, 47 | ) 48 | except Exception as e: 49 | logger.error("Assume role error %s", e) 50 | 51 | else: 52 | msk = boto3.client('kafka', region_name=region) 53 | 54 | try: 55 | clusterslist = msk.list_clusters() 56 | except botocore.exceptions.ClientError as e: 57 | if e.response['Error']['Code'] == 'AuthFailure': 58 | print('Region {region} seems to be disabled for this account, skipping') 59 | continue 60 | except: 61 | raise e 62 | else: 63 | clusterslist = clusterslist["ClusterInfoList"] 64 | 65 | versions = get_versions(msk) 66 | versions_len = len(versions) 67 | 68 | for cluster in clusterslist: 69 | for i in range(versions_len): 70 | if cluster['CurrentBrokerSoftwareInfo']['KafkaVersion'] == versions[i]: 71 | versions_back = i 72 | 73 | resource_output[cluster['ClusterName']] = {'Check': 'msk', 74 | 'Account': id, 75 | 'Region': region, 76 | 'Resource ID': cluster['ClusterName'], 77 | 'Sub Type': 'kafka', 78 | 'Deployed Version': cluster['CurrentBrokerSoftwareInfo']['KafkaVersion'], 79 | 'Latest Version': versions[0], 80 | 'Versions Back': versions_back 81 | } 82 | 83 | return resource_output 84 | 85 | def get_versions(msk): 86 | 87 | kafka_versions = msk.list_kafka_versions() 88 | msk_versions = [] 89 | 90 | for versions in kafka_versions['KafkaVersions']: 91 | msk_versions.append(versions['Version']) 92 | msk_versions.sort(key=versioncmp, reverse=True) 93 | return msk_versions 94 | -------------------------------------------------------------------------------- /version_msk/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_msk/requirements.txt -------------------------------------------------------------------------------- /version_rds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_rds/__init__.py -------------------------------------------------------------------------------- /version_rds/app.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import logging 3 | import botocore.exceptions 4 | from distutils.version import LooseVersion as versioncmp 5 | 6 | def lambda_handler(event, context): 7 | 8 | logger = logging.getLogger() 9 | logger.setLevel(logging.INFO) 10 | 11 | master_session = boto3.session.Session() 12 | sts = master_session.client('sts') 13 | 14 | current_account = sts.get_caller_identity()['Account'] 15 | 16 | id = event["account"] 17 | regions = boto3.session.Session().get_available_regions('rds') 18 | 19 | resource_output = dict() 20 | 21 | for region in regions: 22 | 23 | if id != current_account: 24 | role = "arn:aws:iam::{}:role/aws-ver-dash-lambda-rds" 25 | role_arn = role.format(id) 26 | try: 27 | assume_role_response = sts.assume_role( 28 | RoleArn=role_arn, RoleSessionName="LambdaExecution") 29 | 30 | sts_connection = boto3.client('sts') 31 | acct_b = sts_connection.assume_role( 32 | RoleArn=role_arn, 33 | RoleSessionName="cross_acct_lambda" 34 | ) 35 | 36 | ACCESS_KEY = acct_b['Credentials']['AccessKeyId'] 37 | SECRET_KEY = acct_b['Credentials']['SecretAccessKey'] 38 | SESSION_TOKEN = acct_b['Credentials']['SessionToken'] 39 | 40 | # create service client using the assumed role credentials, e.g. S3 41 | rds = boto3.client( 42 | 'rds', 43 | aws_access_key_id=ACCESS_KEY, 44 | aws_secret_access_key=SECRET_KEY, 45 | aws_session_token=SESSION_TOKEN, 46 | region_name=region, 47 | ) 48 | except Exception as e: 49 | logger.error("Assume role error %s", e) 50 | 51 | else: 52 | rds = boto3.client('rds', region_name=region) 53 | 54 | try: 55 | instances = rds.describe_db_instances() 56 | except botocore.exceptions.ClientError as e: 57 | if e.response['Error']['Code'] == 'AuthFailure': 58 | print('Region {region} seems to be disabled for this account, skipping') 59 | continue 60 | except: 61 | raise e 62 | else: 63 | instances = instances["DBInstances"] 64 | 65 | latest_version = dict() 66 | 67 | for instance in instances: 68 | if instance['Engine'] not in latest_version.keys(): 69 | latest_version[instance['Engine']] = get_versions(rds, instance['Engine']) 70 | 71 | latest_version_len = len(latest_version[instance['Engine']]) 72 | for i in range(latest_version_len): 73 | if latest_version[instance['Engine']][i] == instance['EngineVersion']: 74 | versions_back = i 75 | 76 | resource_output[instance['DBInstanceIdentifier']] = {'Check': 'rds', 77 | 'Account': id, 78 | 'Region': region, 79 | 'Resource ID': instance['DBInstanceIdentifier'], 80 | 'Sub Type': instance['Engine'], 81 | 'Deployed Version': instance['EngineVersion'], 82 | 'Latest Version': latest_version[instance['Engine']][0], 83 | 'Versions Back': versions_back 84 | } 85 | 86 | return resource_output 87 | 88 | def get_versions(rds, engine_type): 89 | 90 | rds_versions = rds.describe_db_engine_versions(Engine=engine_type) 91 | engine_versions = [] 92 | 93 | for versions in rds_versions['DBEngineVersions']: 94 | if engine_type == 'aurora' and 'aurora' not in versions['EngineVersion']: 95 | pass 96 | elif engine_type == 'aurora-mysql' and 'aurora' not in versions['EngineVersion']: 97 | pass 98 | else: 99 | engine_versions.append(versions['EngineVersion']) 100 | 101 | engine_versions.sort(key=versioncmp, reverse=True) 102 | if engine_type == 'aurora-mysql': 103 | engine_versions.append('5.7.12') 104 | if engine_type == 'aurora': 105 | engine_versions.append('5.6.10a') 106 | 107 | return engine_versions -------------------------------------------------------------------------------- /version_rds/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-version-dashboard/44b523b95c1fbe34770d88b81f0268f2bd6b62c4/version_rds/requirements.txt -------------------------------------------------------------------------------- /version_state_machine/version_state_machine.asl.json: -------------------------------------------------------------------------------- 1 | { 2 | "StartAt": "GetAccountList", 3 | "States": { 4 | "GetAccountList": { 5 | "Type": "Task", 6 | "Resource":"${VersionAcctListFunctionArn}", 7 | "Next":"PullDataFromAccounts"}, 8 | "PullDataFromAccounts": { 9 | "Type": "Map", 10 | "ItemsPath": "$.accounts", 11 | "MaxConcurrency": 10, 12 | "Iterator": { 13 | "StartAt": "GetVersions", 14 | "States": { 15 | "GetVersions": { 16 | "Type": "Parallel", 17 | "Next": "CombineVersions", 18 | "Branches": [ 19 | { 20 | "StartAt": "GetRDSVersion", 21 | "States": { 22 | "GetRDSVersion": { 23 | "Type": "Task", 24 | "Resource": "${VersionRdsFunctionArn}", 25 | "Retry": [ 26 | { 27 | "ErrorEquals": [ 28 | "Lambda.ServiceException", 29 | "Lambda.AWSLambdaException", 30 | "Lambda.SdkClientException" 31 | ], 32 | "IntervalSeconds": 2, 33 | "MaxAttempts": 3, 34 | "BackoffRate": 2 35 | } 36 | ], 37 | "End": true 38 | } 39 | } 40 | }, 41 | { 42 | "StartAt": "GetElasticsearchVersion", 43 | "States": { 44 | "GetElasticsearchVersion": { 45 | "Type": "Task", 46 | "Resource": "${VersionEcFunctionArn}", 47 | "Retry": [ 48 | { 49 | "ErrorEquals": [ 50 | "Lambda.ServiceException", 51 | "Lambda.AWSLambdaException", 52 | "Lambda.SdkClientException" 53 | ], 54 | "IntervalSeconds": 2, 55 | "MaxAttempts": 3, 56 | "BackoffRate": 2 57 | } 58 | ], 59 | "End": true 60 | } 61 | } 62 | }, 63 | { 64 | "StartAt": "GetDocumentDBVersion", 65 | "States": { 66 | "GetDocumentDBVersion": { 67 | "Type": "Task", 68 | "Resource": "${VersionDocDBFunctionArn}", 69 | "Retry": [ 70 | { 71 | "ErrorEquals": [ 72 | "Lambda.ServiceException", 73 | "Lambda.AWSLambdaException", 74 | "Lambda.SdkClientException" 75 | ], 76 | "IntervalSeconds": 2, 77 | "MaxAttempts": 3, 78 | "BackoffRate": 2 79 | } 80 | ], 81 | "End": true 82 | } 83 | } 84 | }, 85 | { 86 | "StartAt": "GetEKSVersion", 87 | "States": { 88 | "GetEKSVersion": { 89 | "Type": "Task", 90 | "Resource": "${VersionEKSFunctionArn}", 91 | "Retry": [ 92 | { 93 | "ErrorEquals": [ 94 | "Lambda.ServiceException", 95 | "Lambda.AWSLambdaException", 96 | "Lambda.SdkClientException" 97 | ], 98 | "IntervalSeconds": 2, 99 | "MaxAttempts": 3, 100 | "BackoffRate": 2 101 | } 102 | ], 103 | "End": true 104 | } 105 | } 106 | }, 107 | { 108 | "StartAt": "GetMSKVersion", 109 | "States": { 110 | "GetMSKVersion": { 111 | "Type": "Task", 112 | "Resource": "${VersionMSKFunctionArn}", 113 | "Retry": [ 114 | { 115 | "ErrorEquals": [ 116 | "Lambda.ServiceException", 117 | "Lambda.AWSLambdaException", 118 | "Lambda.SdkClientException" 119 | ], 120 | "IntervalSeconds": 2, 121 | "MaxAttempts": 3, 122 | "BackoffRate": 2 123 | } 124 | ], 125 | "End": true 126 | } 127 | } 128 | }, 129 | { 130 | "StartAt": "GetMQVersion", 131 | "States": { 132 | "GetMQVersion": { 133 | "Type": "Task", 134 | "Resource": "${VersionMQFunctionArn}", 135 | "Retry": [ 136 | { 137 | "ErrorEquals": [ 138 | "Lambda.ServiceException", 139 | "Lambda.AWSLambdaException", 140 | "Lambda.SdkClientException" 141 | ], 142 | "IntervalSeconds": 2, 143 | "MaxAttempts": 3, 144 | "BackoffRate": 2 145 | } 146 | ], 147 | "End": true 148 | } 149 | } 150 | }, 151 | { 152 | "StartAt": "GetElasticacheVersion", 153 | "States": { 154 | "GetElasticacheVersion": { 155 | "Type": "Task", 156 | "Resource": "${VersionEcFunctionArn}", 157 | "Retry": [ 158 | { 159 | "ErrorEquals": [ 160 | "Lambda.ServiceException", 161 | "Lambda.AWSLambdaException", 162 | "Lambda.SdkClientException" 163 | ], 164 | "IntervalSeconds": 2, 165 | "MaxAttempts": 3, 166 | "BackoffRate": 2 167 | } 168 | ], 169 | "End": true 170 | } 171 | } 172 | } 173 | ] 174 | }, 175 | "CombineVersions": { 176 | "Type": "Task", 177 | "Resource": "${VersionCombineFunctionArn}", 178 | "Retry": [ 179 | { 180 | "ErrorEquals": [ 181 | "Lambda.ServiceException", 182 | "Lambda.AWSLambdaException", 183 | "Lambda.SdkClientException" 184 | ], 185 | "IntervalSeconds": 2, 186 | "MaxAttempts": 3, 187 | "BackoffRate": 2 188 | } 189 | ], 190 | "End": true 191 | } 192 | } 193 | }, 194 | "ResultPath": "$.detail.processedItems", 195 | "Next": "CombineAllAccounts" 196 | }, 197 | "CombineAllAccounts": { 198 | "Type": "Task", 199 | "Resource": "${VersionCombineAcctsFunctionArn}", 200 | "End": true 201 | } 202 | } 203 | } --------------------------------------------------------------------------------