cid-cmd 4.3.3__tar.gz → 4.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {cid_cmd-4.3.3/cid_cmd.egg-info → cid_cmd-4.3.6}/PKG-INFO +11 -11
  2. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/README.md +10 -10
  3. cid_cmd-4.3.6/cid/__init__.py +6 -0
  4. cid_cmd-4.3.6/cid/_version.py +2 -0
  5. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/resources.yaml +0 -12
  6. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/cli.py +1 -0
  7. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/common.py +49 -31
  8. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/export.py +9 -3
  9. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/account_map.py +5 -9
  10. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/cur.py +23 -13
  11. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/__init__.py +10 -13
  12. cid_cmd-4.3.6/cid/plugin.py +62 -0
  13. {cid_cmd-4.3.3 → cid_cmd-4.3.6/cid_cmd.egg-info}/PKG-INFO +11 -11
  14. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid_cmd.egg-info/SOURCES.txt +0 -3
  15. cid_cmd-4.3.3/cid/__init__.py +0 -3
  16. cid_cmd-4.3.3/cid/_version.py +0 -2
  17. cid_cmd-4.3.3/cid/builtin/core/data/datasets/tao/dataset.json +0 -1684
  18. cid_cmd-4.3.3/cid/builtin/core/data/queries/tao/glue_table.json +0 -982
  19. cid_cmd-4.3.3/cid/builtin/core/data/queries/tao/ta_org_view.sql +0 -247
  20. cid_cmd-4.3.3/cid/plugin.py +0 -70
  21. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/LICENSE +0 -0
  22. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/MANIFEST.in +0 -0
  23. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/base.py +0 -0
  24. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/__init__.py +0 -0
  25. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/__init__.py +0 -0
  26. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/cid/compute.json +0 -0
  27. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/cid/ec2_running_cost.json +0 -0
  28. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/cid/s3_view.json +0 -0
  29. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/cid/summary_view.json +0 -0
  30. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/co/dataset.json +0 -0
  31. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/cudos/hourly_view.json +0 -0
  32. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/cudos/resource_view.json +0 -0
  33. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/kpi/kpi_ebs_snap.json +0 -0
  34. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/kpi/kpi_ebs_storage_all.json +0 -0
  35. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/kpi/kpi_instance_all.json +0 -0
  36. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/kpi/kpi_s3_storage_all.json +0 -0
  37. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/kpi/kpi_tracker.json +0 -0
  38. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/shared/customer_all.json +0 -0
  39. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/trends/daily_anomaly_detection.json +0 -0
  40. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/trends/monthly_anomaly_detection.json +0 -0
  41. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/datasets/trends/monthly_bill_by_account.json +0 -0
  42. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/permissions/dashboard_permissions.json +0 -0
  43. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/permissions/dashboard_permissions_namespace.json +0 -0
  44. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/permissions/data_set_permissions.json +0 -0
  45. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/permissions/data_set_rls_permissions.json +0 -0
  46. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/permissions/data_source_permissions.json +0 -0
  47. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/permissions/folder_permissions.json +0 -0
  48. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/cid/compute_savings_plan_eligible_spend.sql +0 -0
  49. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/cid/ec2_running_cost.sql +0 -0
  50. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/cid/ri_sp_mapping.sql +0 -0
  51. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/cid/s3.sql +0 -0
  52. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/cid/summary_view.sql +0 -0
  53. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/all_options.sql +0 -0
  54. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/auto_scale.json +0 -0
  55. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/auto_scale_options.sql +0 -0
  56. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/ebs_volume.json +0 -0
  57. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/ebs_volume_options.sql +0 -0
  58. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/ec2_instance.json +0 -0
  59. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/ec2_instance_options.sql +0 -0
  60. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/ecs_service.json +0 -0
  61. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/ecs_service_options.sql +0 -0
  62. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/idle.json +0 -0
  63. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/idle_options.sql +0 -0
  64. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/lambda.json +0 -0
  65. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/lambda_options.sql +0 -0
  66. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/license.json +0 -0
  67. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/license_options.sql +0 -0
  68. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/rds_database.json +0 -0
  69. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/rds_instance_options.sql +0 -0
  70. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/co/rds_storage_options.sql +0 -0
  71. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/cudos/hourly_view.sql +0 -0
  72. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/cudos/resource_view.sql +0 -0
  73. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/kpi/first_kpi_instance_mapping_view.sql +0 -0
  74. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/kpi/kpi_ebs_snap_view.sql +0 -0
  75. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/kpi/kpi_ebs_storage_view.sql +0 -0
  76. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/kpi/kpi_instance_all_view.sql +0 -0
  77. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/kpi/kpi_s3_storage_view.sql +0 -0
  78. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/kpi/last_kpi_tracker_view.sql +0 -0
  79. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/account_map.sql +0 -0
  80. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/account_map_cur2.sql +0 -0
  81. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/account_map_dummy.sql +0 -0
  82. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/aws_accounts.sql +0 -0
  83. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/aws_regions.sql +0 -0
  84. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/aws_service_category_map.sql +0 -0
  85. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/business_units_map.sql +0 -0
  86. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/cur.yaml +0 -0
  87. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/customer_all_unlimited.sql +0 -0
  88. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/payer_account_name_map.sql +0 -0
  89. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/shared/ta_descriptions.sql +0 -0
  90. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/trends/daily_anomaly_detection.sql +0 -0
  91. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/trends/monthly_anomaly_detection.sql +0 -0
  92. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/builtin/core/data/queries/trends/monthly_bill_by_account.sql +0 -0
  93. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/commands/__init__.py +0 -0
  94. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/commands/command_base.py +0 -0
  95. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/commands/init_qs.py +0 -0
  96. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/exceptions.py +0 -0
  97. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/__init__.py +0 -0
  98. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/athena.py +0 -0
  99. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/cloudformation.py +0 -0
  100. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/csv2view.py +0 -0
  101. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/cur_proxy.py +0 -0
  102. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/diff.py +0 -0
  103. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/glue.py +0 -0
  104. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/iam.py +0 -0
  105. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/organizations.py +0 -0
  106. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/parameter_store.py +0 -0
  107. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/dashboard.py +0 -0
  108. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/dashboard_patching.py +0 -0
  109. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/dataset.py +0 -0
  110. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/datasource.py +0 -0
  111. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/definition.py +0 -0
  112. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/resource.py +0 -0
  113. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/template.py +0 -0
  114. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/quicksight/version.py +0 -0
  115. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/randtime.py +0 -0
  116. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/s3.py +0 -0
  117. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/helpers/timezone.py +0 -0
  118. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/logger.py +0 -0
  119. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/test/python/test_csv2view.py +0 -0
  120. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/test/python/test_isolated_parameters.py +0 -0
  121. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/test/python/test_merge.py +0 -0
  122. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid/utils.py +0 -0
  123. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid_cmd.egg-info/dependency_links.txt +0 -0
  124. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid_cmd.egg-info/entry_points.txt +0 -0
  125. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid_cmd.egg-info/requires.txt +0 -0
  126. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/cid_cmd.egg-info/top_level.txt +0 -0
  127. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/pyproject.toml +0 -0
  128. {cid_cmd-4.3.3 → cid_cmd-4.3.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cid-cmd
3
- Version: 4.3.3
3
+ Version: 4.3.6
4
4
  Summary: Cloud Intelligence Dashboards deployment helper tool
5
5
  Home-page: https://github.com/aws-samples/aws-cudos-framework-deployment
6
6
  Author: AWS CUDOS Team
@@ -47,7 +47,7 @@ Dynamic: license-file
47
47
  1. [Notices](#Notices)
48
48
 
49
49
  ## Overview
50
- The Cloud Intelligence Dashboards is an open-source framework, lovingly cultivated and maintained by a group of customer-obsessed AWSers, that gives customers the power to get high-level and granular insight into their cost and usage data. Supported by the Well-Architected framework, the dashboards can be deployed by any customer using a CloudFormation template or a command-line tool in their environment in under 30 minutes. These dashboards help you to drive financial accountability, optimize cost, track usage goals, implement best-practices for governance, and achieve operational excellence across all your organization.
50
+ The Cloud Intelligence Dashboards is an open-source framework, lovingly cultivated and maintained by a group of customer-obsessed AWSers, that gives customers the power to get high-level and granular insight into their cost and usage data. Supported by the Well-Architected framework, the dashboards can be deployed by any customer using a CloudFormation template or a command-line tool in their environment in under 30 minutes. These dashboards help you to drive financial accountability, optimize cost, track usage goals, implement best practices for governance, and achieve operational excellence across all your organization.
51
51
 
52
52
  Cloud Intelligence Dashboards Framework provides AWS customers with [more then 20 Dashboards](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/dashboards.html).
53
53
  * Foundational Dashboards - A set of main Dashboards that only require Cost and Usage Report(CUR)
@@ -81,7 +81,7 @@ This foundational architecture is recommended for starting and allows deployment
81
81
  5. User Teams (Executives, FinOps, Engineers) can access Cloud Intelligence Dashboards in [Amazon QuickSight](https://aws.amazon.com/quicksight/). Access is secured through [AWS IAM](https://aws.amazon.com/iam/), IIC ([AWS IAM Identity Center](https://aws.amazon.com/iam/identity-center/), formerly SSO), and optional [Row Level Security](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/row-level-security.html).
82
82
  6. Optionally, the Advanced data collection can be deployed to enable advanced dashboards based on [AWS Trusted Advisor](https://aws.amazon.com/premiumsupport/trustedadvisor/), [AWS Health](https://aws.amazon.com/premiumsupport/technology/aws-health-dashboard/) Events and other sources. Additional data is retrieved from [AWS Organizations](https://aws.amazon.com/organizations/) or Linked Accounts. In this case [Amazon EventBridge](https://aws.amazon.com/eventbridge/) rule triggers an [AWS Step Functions](https://aws.amazon.com/step-functions/) workflow for data collection modules on a configurable schedule.
83
83
  7. The "Account Collector" [AWS Lambda](https://aws.amazon.com/lambda/) in AWS Step Functions retrieves linked account details using AWS Organizations API.
84
- 8. The "Data Collection" Lambda function in AWS Step Functions assumes role in each linked account to retrieve account-specific data via [AWS SDK](https://aws.amazon.com/developer/tools/).
84
+ 8. The "Data Collection" Lambda function in AWS Step Functions assumes a role in each linked account to retrieve account-specific data via [AWS SDK](https://aws.amazon.com/developer/tools/).
85
85
  9. Retrieved data is stored in a centralized Amazon S3 Bucket.
86
86
  10. [Advanced Cloud Intelligence Dashboards](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/dashboards.html#advanced-dashboards) leverage Amazon Athena and Amazon QuickSight for comprehensive data analysis.
87
87
 
@@ -102,16 +102,16 @@ The following table provides a sample cost breakdown for deploying of Foundation
102
102
  | **Total Estimated Monthly Cost** | | **$100-$200** |
103
103
 
104
104
  \* Costs are relative to the size of your Cost and Usage Report (CUR) data
105
- \** Costs are relative to number of Users
105
+ \** Costs are relative to the number of users
106
106
 
107
107
  **Additional Notes:**
108
108
  - Free trial available for 30 days for 4 QuickSight users
109
109
  - Actual costs may vary based on specific usage and data volume
110
110
 
111
- Pleas use AWS Pricing Calculator for precise estimation.
111
+ Please use the AWS Pricing Calculator for precise estimation.
112
112
 
113
113
  ## Prerequisites
114
- You need access to AWS Accounts. We recommend deployment of the Dashboards in a dedicated Data Collection Account, other than your Management (Payer) Account. We provide provides a CloudFormation templates to copy CUR 2.0 data from your Management Account to a dedicated one. You can use it to aggregate data from multiple Management (Payer) Accounts or multiple Linked Accounts.
114
+ You need access to AWS Accounts. We recommend deployment of the Dashboards in a dedicated Data Collection Account, other than your Management (Payer) Account. We provide CloudFormation templates to copy CUR 2.0 data from your Management Account to a dedicated one. You can use it to aggregate data from multiple Management (Payer) Accounts or multiple Linked Accounts.
115
115
 
116
116
  If you do not have access to the Management/Payer Account, you can still collect the data across multiple Linked accounts using the same approach.
117
117
 
@@ -119,7 +119,7 @@ The ownership of CID is usually with the FinOps team, who do not have administra
119
119
 
120
120
 
121
121
  ## Regions
122
- Make sure you are installing data collection in the same region where you are going to use the data to avoid cross region charges.
122
+ Make sure you are installing data collection in the same region where you are going to use the data to avoid cross-region charges.
123
123
  CFN deployment is only available in a limited number of regions, while CLI deployment is region agnostic.
124
124
 
125
125
  | Region Name | Region Code | Support CLI | Support CFN |
@@ -151,7 +151,7 @@ CFN deployment is only available in a limited number of regions, while CLI deplo
151
151
 
152
152
  ## Deployment Steps
153
153
  There are several ways we can deploy dashboards:
154
- 1. Using cid-cmd tool from command line
154
+ 1. Using cid-cmd tool from the command line
155
155
  1. [CloudFormation Template](./cfn-templates/cid-cfn.yml) using cid-cmd tool in Amazon Lambda. (Recommended)
156
156
 
157
157
  Please refer to the deployment documentation [here](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/deployment-in-global-regions.html).
@@ -166,8 +166,8 @@ Please refer to the documentation [here](https://docs.aws.amazon.com/guidance/la
166
166
  Please refer to the documentation [here](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/faq.html).
167
167
 
168
168
  ## Changelogs
169
- For dashboards please check Change Logs [here](changes/)
170
- For CID deployment tool, including Cli and CFN please check [Releases](/releases)
169
+ For dashboards please check change log [here](changes/)
170
+ For CID deployment tool, including CLI and CFN please check [Releases](/releases)
171
171
 
172
172
  ## Feedback
173
173
  Please reference to [this page](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/feedback-support.html)
@@ -184,4 +184,4 @@ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more inform
184
184
  This library is licensed under the MIT-0 License. See the [LICENSE](LICENSE) file.
185
185
 
186
186
  ## Notices
187
- Dashboards and their content: (a) are for informational purposes only, (b) represents current AWS product offerings and practices, which are subject to change without notice, and (c) does not create any commitments or assurances from AWS and its affiliates, suppliers or licensors. AWS content, products or services are provided “as is” without warranties, representations, or conditions of any kind, whether express or implied. The responsibilities and liabilities of AWS to its customers are controlled by AWS agreements, and this document is not part of, nor does it modify, any agreement between AWS and its customers.
187
+ Dashboards and their content: (a) are for informational purposes only, (b) represent current AWS product offerings and practices, which are subject to change without notice, and (c) does not create any commitments or assurances from AWS and its affiliates, suppliers or licensors. AWS content, products or services are provided “as is” without warranties, representations, or conditions of any kind, whether express or implied. The responsibilities and liabilities of AWS to its customers are controlled by AWS agreements, and this document is not part of, nor does it modify, any agreement between AWS and its customers.
@@ -19,7 +19,7 @@
19
19
  1. [Notices](#Notices)
20
20
 
21
21
  ## Overview
22
- The Cloud Intelligence Dashboards is an open-source framework, lovingly cultivated and maintained by a group of customer-obsessed AWSers, that gives customers the power to get high-level and granular insight into their cost and usage data. Supported by the Well-Architected framework, the dashboards can be deployed by any customer using a CloudFormation template or a command-line tool in their environment in under 30 minutes. These dashboards help you to drive financial accountability, optimize cost, track usage goals, implement best-practices for governance, and achieve operational excellence across all your organization.
22
+ The Cloud Intelligence Dashboards is an open-source framework, lovingly cultivated and maintained by a group of customer-obsessed AWSers, that gives customers the power to get high-level and granular insight into their cost and usage data. Supported by the Well-Architected framework, the dashboards can be deployed by any customer using a CloudFormation template or a command-line tool in their environment in under 30 minutes. These dashboards help you to drive financial accountability, optimize cost, track usage goals, implement best practices for governance, and achieve operational excellence across all your organization.
23
23
 
24
24
  Cloud Intelligence Dashboards Framework provides AWS customers with [more then 20 Dashboards](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/dashboards.html).
25
25
  * Foundational Dashboards - A set of main Dashboards that only require Cost and Usage Report(CUR)
@@ -53,7 +53,7 @@ This foundational architecture is recommended for starting and allows deployment
53
53
  5. User Teams (Executives, FinOps, Engineers) can access Cloud Intelligence Dashboards in [Amazon QuickSight](https://aws.amazon.com/quicksight/). Access is secured through [AWS IAM](https://aws.amazon.com/iam/), IIC ([AWS IAM Identity Center](https://aws.amazon.com/iam/identity-center/), formerly SSO), and optional [Row Level Security](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/row-level-security.html).
54
54
  6. Optionally, the Advanced data collection can be deployed to enable advanced dashboards based on [AWS Trusted Advisor](https://aws.amazon.com/premiumsupport/trustedadvisor/), [AWS Health](https://aws.amazon.com/premiumsupport/technology/aws-health-dashboard/) Events and other sources. Additional data is retrieved from [AWS Organizations](https://aws.amazon.com/organizations/) or Linked Accounts. In this case [Amazon EventBridge](https://aws.amazon.com/eventbridge/) rule triggers an [AWS Step Functions](https://aws.amazon.com/step-functions/) workflow for data collection modules on a configurable schedule.
55
55
  7. The "Account Collector" [AWS Lambda](https://aws.amazon.com/lambda/) in AWS Step Functions retrieves linked account details using AWS Organizations API.
56
- 8. The "Data Collection" Lambda function in AWS Step Functions assumes role in each linked account to retrieve account-specific data via [AWS SDK](https://aws.amazon.com/developer/tools/).
56
+ 8. The "Data Collection" Lambda function in AWS Step Functions assumes a role in each linked account to retrieve account-specific data via [AWS SDK](https://aws.amazon.com/developer/tools/).
57
57
  9. Retrieved data is stored in a centralized Amazon S3 Bucket.
58
58
  10. [Advanced Cloud Intelligence Dashboards](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/dashboards.html#advanced-dashboards) leverage Amazon Athena and Amazon QuickSight for comprehensive data analysis.
59
59
 
@@ -74,16 +74,16 @@ The following table provides a sample cost breakdown for deploying of Foundation
74
74
  | **Total Estimated Monthly Cost** | | **$100-$200** |
75
75
 
76
76
  \* Costs are relative to the size of your Cost and Usage Report (CUR) data
77
- \** Costs are relative to number of Users
77
+ \** Costs are relative to the number of users
78
78
 
79
79
  **Additional Notes:**
80
80
  - Free trial available for 30 days for 4 QuickSight users
81
81
  - Actual costs may vary based on specific usage and data volume
82
82
 
83
- Pleas use AWS Pricing Calculator for precise estimation.
83
+ Please use the AWS Pricing Calculator for precise estimation.
84
84
 
85
85
  ## Prerequisites
86
- You need access to AWS Accounts. We recommend deployment of the Dashboards in a dedicated Data Collection Account, other than your Management (Payer) Account. We provide provides a CloudFormation templates to copy CUR 2.0 data from your Management Account to a dedicated one. You can use it to aggregate data from multiple Management (Payer) Accounts or multiple Linked Accounts.
86
+ You need access to AWS Accounts. We recommend deployment of the Dashboards in a dedicated Data Collection Account, other than your Management (Payer) Account. We provide CloudFormation templates to copy CUR 2.0 data from your Management Account to a dedicated one. You can use it to aggregate data from multiple Management (Payer) Accounts or multiple Linked Accounts.
87
87
 
88
88
  If you do not have access to the Management/Payer Account, you can still collect the data across multiple Linked accounts using the same approach.
89
89
 
@@ -91,7 +91,7 @@ The ownership of CID is usually with the FinOps team, who do not have administra
91
91
 
92
92
 
93
93
  ## Regions
94
- Make sure you are installing data collection in the same region where you are going to use the data to avoid cross region charges.
94
+ Make sure you are installing data collection in the same region where you are going to use the data to avoid cross-region charges.
95
95
  CFN deployment is only available in a limited number of regions, while CLI deployment is region agnostic.
96
96
 
97
97
  | Region Name | Region Code | Support CLI | Support CFN |
@@ -123,7 +123,7 @@ CFN deployment is only available in a limited number of regions, while CLI deplo
123
123
 
124
124
  ## Deployment Steps
125
125
  There are several ways we can deploy dashboards:
126
- 1. Using cid-cmd tool from command line
126
+ 1. Using cid-cmd tool from the command line
127
127
  1. [CloudFormation Template](./cfn-templates/cid-cfn.yml) using cid-cmd tool in Amazon Lambda. (Recommended)
128
128
 
129
129
  Please refer to the deployment documentation [here](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/deployment-in-global-regions.html).
@@ -138,8 +138,8 @@ Please refer to the documentation [here](https://docs.aws.amazon.com/guidance/la
138
138
  Please refer to the documentation [here](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/faq.html).
139
139
 
140
140
  ## Changelogs
141
- For dashboards please check Change Logs [here](changes/)
142
- For CID deployment tool, including Cli and CFN please check [Releases](/releases)
141
+ For dashboards please check change log [here](changes/)
142
+ For CID deployment tool, including CLI and CFN please check [Releases](/releases)
143
143
 
144
144
  ## Feedback
145
145
  Please reference to [this page](https://docs.aws.amazon.com/guidance/latest/cloud-intelligence-dashboards/feedback-support.html)
@@ -156,4 +156,4 @@ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more inform
156
156
  This library is licensed under the MIT-0 License. See the [LICENSE](LICENSE) file.
157
157
 
158
158
  ## Notices
159
- Dashboards and their content: (a) are for informational purposes only, (b) represents current AWS product offerings and practices, which are subject to change without notice, and (c) does not create any commitments or assurances from AWS and its affiliates, suppliers or licensors. AWS content, products or services are provided “as is” without warranties, representations, or conditions of any kind, whether express or implied. The responsibilities and liabilities of AWS to its customers are controlled by AWS agreements, and this document is not part of, nor does it modify, any agreement between AWS and its customers.
159
+ Dashboards and their content: (a) are for informational purposes only, (b) represent current AWS product offerings and practices, which are subject to change without notice, and (c) does not create any commitments or assurances from AWS and its affiliates, suppliers or licensors. AWS content, products or services are provided “as is” without warranties, representations, or conditions of any kind, whether express or implied. The responsibilities and liabilities of AWS to its customers are controlled by AWS agreements, and this document is not part of, nor does it modify, any agreement between AWS and its customers.
@@ -0,0 +1,6 @@
1
+ # Declare namespace
2
+
3
+ try:
4
+ __import__('pkgutil').extend_path(__path__, __name__)
5
+ except NameError:
6
+ pass
@@ -0,0 +1,2 @@
1
+ __version__ = '4.3.6'
2
+
@@ -75,18 +75,6 @@ dashboards:
75
75
  'item_description', 'pricing_unit', 'region', 'pricing_term', 'linked_account_id', 'savings_plan_a_r_n', 'tags_json'
76
76
  ]
77
77
 
78
- TAO:
79
- category: 'Advanced'
80
- name: Trusted Advisor Organizational View
81
- templateId: ta-organizational-view
82
- dashboardId: ta-organizational-view
83
- dependsOn:
84
- datasets:
85
- - ta-organizational-view
86
- versions:
87
- minTemplateVersion: 13
88
- minTemplateDescription: "v1.4.0"
89
-
90
78
  Trends:
91
79
  category: 'Additional'
92
80
  name: Trends Dashboard
@@ -167,6 +167,7 @@ def export(ctx, **kwargs):
167
167
  (definition|template) A method (definition=pull json definition of Analysis OR template=create QuickSight Template)
168
168
  --export-known-datasets
169
169
  (no|yes) If 'yes' the export will include DataSets that are already in resources file. Default = no
170
+ --export-tables (no|yes). If 'yes' the export will include Tables. Default: no = only Views
170
171
  --category TEXT The dashboards category. Default = Custom
171
172
  --output A filename (.yaml) If provided an existing file it will be analyzed for default values and overridden
172
173
  """
@@ -6,7 +6,7 @@ import functools
6
6
  import webbrowser
7
7
  from string import Template
8
8
  from typing import Dict
9
- from pkg_resources import resource_string
9
+ from importlib import resources
10
10
  from importlib.metadata import entry_points
11
11
  from functools import cached_property
12
12
 
@@ -433,12 +433,21 @@ class Cid():
433
433
  except (self.athena.client.exceptions.ClientError, CidError, CidCritical) as exc:
434
434
  raise CidCritical(f'Failed fetching parameter {prefix}{key}: {exc}.') from exc
435
435
  if not res_list:
436
- raise CidCritical(f'Failed fetching parameter {prefix}{key}, {value}. Athena returns empty results. {value.get("error")}')
437
- elif len(res_list) == 1:
438
- params[key] = '-'.join(res_list[0])
436
+ raise CidCritical(f'Failed fetching parameter {prefix}{key}, {value}. Athena returns empty results. {value.get("error", "")}')
437
+ options = ['-'.join(res) for res in res_list]
438
+ default = value.get('default', '{default not provided}')
439
+ if len(options) == 1:
440
+ # silently taking the 1st available option
441
+ params[key] = options[0]
442
+ elif not utils.isatty():
443
+ if default in options:
444
+ # silently taking the default option if we cannot ask user
445
+ params[key] = default
446
+ else:
447
+ # silently taking the first option
448
+ params[key] = sorted(options)[0]
439
449
  else:
440
- options = ['-'.join(res) for res in res_list]
441
- default = value.get('default')
450
+ # asking user
442
451
  params[key] = get_parameter(
443
452
  param_name=prefix + key,
444
453
  message=f"Required parameter: {key} ({value.get('description')})",
@@ -624,7 +633,16 @@ class Cid():
624
633
  dataset = self.qs.describe_dataset(id=dashboard_datasets.get(dataset_name), no_cache=True)
625
634
 
626
635
  if not isinstance(dataset, Dataset):
627
- # Second chance: try to find the dataset with the id that is the name
636
+ # Second chance: try to find the dataset with the id from resources
637
+ try:
638
+ _ds_id = self.resources['datasets'].get(dataset_name, {}).get('data', {}).get('DataSetId')
639
+ if _ds_id:
640
+ dataset = self.qs.describe_dataset(id=_ds_id, no_cache=True)
641
+ except Exception as exc:
642
+ logger.debug(f'Failed to describe_dataset {dataset_name} {exc}')
643
+
644
+ if not isinstance(dataset, Dataset):
645
+ # Third chance: try to find the dataset with the id that is the name
628
646
  try:
629
647
  dataset = self.qs.describe_dataset(id=dataset_name, no_cache=True)
630
648
  except Exception as exc:
@@ -662,14 +680,18 @@ class Cid():
662
680
 
663
681
  if not matching_datasets:
664
682
  reco = ''
665
- logger.warning(f'Dataset {dataset_name} is not found.')
666
- if utils.exec_env()['shell'] == 'lambda':
667
- # We are in lambda
668
- reco = 'You can try deleting existing dataset and re-run.'
669
- else:
670
- # We are in command line mode
671
- reco = 'Please retry with --update "yes" --force --recursive flags.'
672
- raise CidCritical(f'Failed to find a Dataset "{dataset_name}" with required fields. ' + reco)
683
+ new_datasets = self.create_datasets([dataset_name], known_datasets=dashboard_datasets, recursive=recursive, update=update)
684
+ if not new_datasets:
685
+ logger.warning(f'Dataset {dataset_name} is not found.')
686
+ if utils.exec_env()['shell'] == 'lambda':
687
+ # We are in lambda
688
+ reco = 'You can try deleting existing dataset and re-run.'
689
+ else:
690
+ # We are in command line mode
691
+ reco = 'Please retry with --update "yes" --force --recursive flags.'
692
+ raise CidCritical(f'Failed to find a Dataset "{dataset_name}" with required fields. ' + reco)
693
+ _ds_id = self.resources['datasets'].get(dataset_name, {}).get('data', {}).get('DataSetId')
694
+ dashboard_definition['datasets'][dataset_name] = f'arn:{self.base.partition}:quicksight:{self.base.region}:{self.base.account_id}:dataset/{_ds_id}'
673
695
  elif len(matching_datasets) >= 1:
674
696
  if len(matching_datasets) > 1:
675
697
  # FIXME: propose a choice?
@@ -1018,10 +1040,9 @@ class Cid():
1018
1040
  param_name='folder-name',
1019
1041
  message='Please enter the folder name to create'
1020
1042
  )
1021
- folder_permissions_tpl = Template(resource_string(
1022
- package_or_requirement='cid.builtin.core',
1023
- resource_name=f'data/permissions/folder_permissions.json',
1024
- ).decode('utf-8'))
1043
+ folder_permissions_tpl = Template(
1044
+ (resources.files('cid.builtin.core') / 'data/permissions/folder_permissions.json').read_text()
1045
+ )
1025
1046
  columns_tpl = {
1026
1047
  'PrincipalArn': self.qs.get_principal_arn()
1027
1048
  }
@@ -1057,10 +1078,9 @@ class Cid():
1057
1078
  columns_tpl = {
1058
1079
  'PrincipalArn': principal_arn
1059
1080
  }
1060
- dashboard_permissions_tpl = Template(resource_string(
1061
- package_or_requirement='cid.builtin.core',
1062
- resource_name=template_filename,
1063
- ).decode('utf-8'))
1081
+ dashboard_permissions_tpl = Template(
1082
+ (resources.files('cid.builtin.core') / template_filename).read_text()
1083
+ )
1064
1084
  dashboard_permissions = json.loads(dashboard_permissions_tpl.safe_substitute(columns_tpl))
1065
1085
  dashboard_params = {
1066
1086
  "GrantPermissions": [
@@ -1085,10 +1105,9 @@ class Cid():
1085
1105
  if share_method == 'account':
1086
1106
  logger.info(f'Sharing datasets/datasources with an account is not supported, skipping')
1087
1107
  else:
1088
- data_set_permissions_tpl = Template(resource_string(
1089
- package_or_requirement='cid.builtin.core',
1090
- resource_name=f'data/permissions/data_set_permissions.json',
1091
- ).decode('utf-8'))
1108
+ data_set_permissions_tpl = Template(
1109
+ (resources.files('cid.builtin.core') / 'data/permissions/data_set_permissions.json').read_text()
1110
+ )
1092
1111
  data_set_permissions = json.loads(data_set_permissions_tpl.safe_substitute(columns_tpl))
1093
1112
 
1094
1113
  _datasources: Dict[str, Datasource] = {}
@@ -1103,10 +1122,9 @@ class Cid():
1103
1122
  if not _datasources.get(_datasource.id):
1104
1123
  _datasources.update({_datasource.id: _datasource})
1105
1124
 
1106
- data_source_permissions_tpl = Template(resource_string(
1107
- package_or_requirement='cid.builtin.core',
1108
- resource_name=f'data/permissions/data_source_permissions.json',
1109
- ).decode('utf-8'))
1125
+ data_source_permissions_tpl = Template(
1126
+ (resources.files('cid.builtin.core') / f'data/permissions/data_source_permissions.json').read_text()
1127
+ )
1110
1128
  data_source_permissions = json.loads(data_source_permissions_tpl.safe_substitute(columns_tpl))
1111
1129
  for k, v in _datasources.items():
1112
1130
  logger.info(f'Sharing data source "{v.name}" ({k})')
@@ -144,10 +144,10 @@ def export_analysis(qs, athena, glue):
144
144
 
145
145
  cid_print(f' Found DataSet <BOLD>{dataset_name}<END>.')
146
146
  if dataset_name in athena._resources.get('datasets'):
147
- resources_datasets.append(dataset_name)
148
- if not get_parameters().get('export-known-datasets'):
147
+ resources_datasets.append(dataset_name) # used later in dependencies
148
+ if not get_parameters().get('export-known-datasets') == 'yes':
149
149
  cid_print(f' DataSet <BOLD>{dataset_name}<END> is in resources. Skipping.')
150
- continue
150
+ continue # prevent export dataset
151
151
 
152
152
  dataset_data = {
153
153
  "DataSetId": dataset.raw['DataSetId'],
@@ -284,7 +284,13 @@ def export_analysis(qs, athena, glue):
284
284
  if key in cur_tables or cur_helper.table_is_cur(name=key):
285
285
  logger.debug(f'Skipping {key} views - it is a CUR')
286
286
  continue
287
+ if key in ['account_map']:
288
+ cid_print(f'{key} is a special data. Processed separately. Skipping.')
289
+ continue
287
290
  if isinstance(view_data.get('data'), str):
291
+ if 'CREATE EXTERNAL TABLE' in view_data.get('data') and not get_parameters().get('export-tables') == 'yes':
292
+ cid_print(f'{key} is a Glue table. Skipping. To export table definitions use `--export-tables yes`')
293
+ continue
288
294
  #check if there is dependency on crawler
289
295
  crawler_names = re.findall(r"UPDATED_BY_CRAWLER\W+?'(.+?)''", view_data.get('data'))
290
296
  if crawler_names:
@@ -38,7 +38,7 @@ from functools import lru_cache
38
38
 
39
39
  from string import Template
40
40
  from boto3.session import Session
41
- from pkg_resources import resource_string
41
+ from importlib import resources
42
42
 
43
43
  from cid.base import CidBase
44
44
  from cid.helpers import Athena, CUR
@@ -119,10 +119,7 @@ class AccountMap(CidBase):
119
119
  if view_definition.get('File'):
120
120
  view_file = view_definition.get('File')
121
121
  template = Template(
122
- resource_string(
123
- view_definition.get('providedBy'),
124
- f'data/{view_file}'
125
- ).decode('utf-8')
122
+ (resources.files(view_definition.get('providedBy')) / f'data/{view_file}').read_text()
126
123
  )
127
124
  elif view_definition.get('data'):
128
125
  template = Template(str(view_definition.get('data')))
@@ -156,10 +153,9 @@ class AccountMap(CidBase):
156
153
  sql_file = 'data/queries/shared/account_map_cur2.sql'
157
154
  else:
158
155
  sql_file = 'data/queries/shared/account_map_dummy.sql'
159
- template = Template(resource_string(
160
- package_or_requirement='cid.builtin.core',
161
- resource_name=sql_file,
162
- ).decode('utf-8'))
156
+ template = Template(
157
+ (resources.files('cid.builtin.core') / sql_file).read_text()
158
+ )
163
159
  columns_tpl = {
164
160
  'athena_view_name': name,
165
161
  'cur_table_name': cur.table_name,
@@ -248,19 +248,24 @@ class CUR(AbstractCUR):
248
248
  def find_cur(self, database: str=None, table: str=None):
249
249
  """Choose CUR"""
250
250
  metadata = None
251
- cur_database = database or get_parameters().get('cur-database')
252
- if table or get_parameters().get('cur-table-name'):
253
- table_name = table or get_parameters().get('cur-table-name')
251
+ cur_database = database or get_parameters().get('cur-database') or self.athena.DatabaseName
252
+ if not table and get_parameters().get('cur-table-name') and ',' not in str(get_parameters().get('cur-table-name')):
253
+ table = get_parameters().get('cur-table-name')
254
+ if table: # Table sepcified explicity, so no need to scan. Just need to make sure the table exists
254
255
  try:
255
- metadata = self.athena.get_table_metadata(table_name, cur_database)
256
+ metadata = self.athena.get_table_metadata(table, cur_database)
256
257
  except self.athena.client.exceptions.MetadataException as exc:
257
- raise CidCritical(f'Provided cur-table-name "{table_name}" in database "{cur_database or self.athena.DatabaseName}" is not found. Please make sure the table exists. This could also indicate a LakeFormation permission issue, see our FAQ for help.') from exc
258
+ logger.debug(f'Provided cur-table-name "{table}" in database "{cur_database}" is not found. ({exc})')
259
+ raise CidCritical(f'Provided cur-table-name "{table}" in database "{cur_database}" is not found. Please make sure the table exists. This could also indicate a LakeFormation permission issue, see our FAQ for help.')
258
260
  res, message = self.table_is_cur(table=metadata, return_reason=True)
259
261
  if not res:
260
- raise CidCritical(f'Table {table_name} does not look like CUR. {message}')
261
- return cur_database or self.athena.DatabaseName, metadata
262
+ raise CidCritical(f'Provided cur-table-name "{table}" in database "{cur_database}" is not cur. {message}')
263
+ return cur_database, metadata
262
264
 
263
265
  all_cur_tables = []
266
+ filter_names = None
267
+ if ',' in str(get_parameters().get('cur-table-name')):
268
+ filter_names = get_parameters().get('cur-table-name').split(',')
264
269
  if cur_database:
265
270
  cur_databases = [cur_database]
266
271
  else:
@@ -271,7 +276,12 @@ class CUR(AbstractCUR):
271
276
  columns=self.cur_minimal_required_columns,
272
277
  database_name=database,
273
278
  )
274
- all_cur_tables += [(database, table['Name']) for table in tables]
279
+ all_cur_tables += [
280
+ (database, table['Name'])
281
+ for table in tables
282
+ if (not filter_names or table['Name'] in filter_names)
283
+ and table['Name'] not in ('cur2_proxy', 'cur2_view')
284
+ ]
275
285
  except self.athena.client.exceptions.ClientError as exc:
276
286
  if 'AccessDenied' in str(exc):
277
287
  logger.info(f'Cannot read from athena database {database}')
@@ -279,10 +289,10 @@ class CUR(AbstractCUR):
279
289
  raise
280
290
 
281
291
  if not all_cur_tables:
282
- # FIXME : distinguish a case where we have NONE tables in any database. This might be because
292
+ # FIXME : distinguish a case where we have NONE tables in any database. This might be because lake formation
283
293
  raise CidCritical(
284
- f'CUR table not found in {self.athena.region}. We need a least one table with these columns: {self.cur_minimal_required_columns}.'
285
- ' Please make sure you created cur and created Athena table, preferably with CID Cloud Formation stack.'
294
+ f'CUR table not found in {self.athena.region}. We need a least one Glue table with these columns: {self.cur_minimal_required_columns}.'
295
+ ' Please make sure you have CUR, preferably with CID CloudFormation stack.'
286
296
  ' Also if you have AWS Lake Formation, the user running the tool might need additional permissions'
287
297
  )
288
298
  databases = set([database for database, _ in all_cur_tables])
@@ -290,7 +300,7 @@ class CUR(AbstractCUR):
290
300
  choices = dict(sorted([(f'{database}.{tab}', (database, tab)) for database, tab in all_cur_tables], reverse=True))
291
301
  else:
292
302
  choices = dict(sorted([(f'{tab}', (database, tab)) for database, tab in all_cur_tables], reverse=True))
293
- if not choices:
303
+ if not choices and not get_parameters().get('no-cur-creation'):
294
304
  choices['<CREATE CUR TABLE AND CRAWLER>'] = '<CREATE CUR TABLE AND CRAWLER>'
295
305
  answer = get_parameter(
296
306
  param_name='cur-table-name-and-db',
@@ -303,7 +313,7 @@ class CUR(AbstractCUR):
303
313
  database, table = answer.split('.')
304
314
  else:
305
315
  database, table = answer
306
- set_parameters({'cur-table-name': table,'cur-database': database, })
316
+ set_parameters({'cur-table-name': table,'cur-database': database})
307
317
  return database, self.athena.get_table_metadata(table, database_name=database)
308
318
 
309
319
  def ensure_column(self, column: str, column_type: str=None):
@@ -6,7 +6,7 @@ import logging
6
6
  from uuid import uuid4
7
7
  from string import Template
8
8
  from typing import Dict, List, Union
9
- from pkg_resources import resource_string
9
+ from importlib import resources
10
10
 
11
11
  from tqdm import tqdm
12
12
 
@@ -352,10 +352,9 @@ class QuickSight(CidBase):
352
352
  columns_tpl = {
353
353
  'PrincipalArn': self.get_principal_arn()
354
354
  }
355
- data_source_permissions_tpl = Template(resource_string(
356
- package_or_requirement='cid.builtin.core',
357
- resource_name='data/permissions/data_source_permissions.json',
358
- ).decode('utf-8'))
355
+ data_source_permissions_tpl = Template(
356
+ (resources.files('cid.builtin.core') / 'data/permissions/data_source_permissions.json').read_text()
357
+ )
359
358
  data_source_permissions = json.loads(data_source_permissions_tpl.safe_substitute(columns_tpl))
360
359
  datasource_name = datasource_id or "CID Athena"
361
360
  datasource_id = datasource_id or str(uuid4())
@@ -1050,10 +1049,9 @@ class QuickSight(CidBase):
1050
1049
  columns_tpl = {
1051
1050
  'PrincipalArn': self.get_principal_arn()
1052
1051
  }
1053
- data_set_permissions_tpl = Template(resource_string(
1054
- package_or_requirement='cid.builtin.core',
1055
- resource_name='data/permissions/data_set_permissions.json',
1056
- ).decode('utf-8'))
1052
+ data_set_permissions_tpl = Template(
1053
+ (resources.files('cid.builtin.core') / 'data/permissions/data_set_permissions.json').read_text()
1054
+ )
1057
1055
  data_set_permissions = json.loads(data_set_permissions_tpl.safe_substitute(columns_tpl))
1058
1056
  definition.update({
1059
1057
  'AwsAccountId': self.account_id,
@@ -1265,10 +1263,9 @@ class QuickSight(CidBase):
1265
1263
 
1266
1264
  create_parameters = self._build_params_for_create_update_dash(definition)
1267
1265
 
1268
- dashboard_permissions_tpl = Template(resource_string(
1269
- package_or_requirement='cid.builtin.core',
1270
- resource_name='data/permissions/dashboard_permissions.json',
1271
- ).decode('utf-8'))
1266
+ dashboard_permissions_tpl = Template(
1267
+ (resources.files('cid.builtin.core') / 'data/permissions/dashboard_permissions.json').read_text()
1268
+ )
1272
1269
  columns_tpl = {
1273
1270
  'PrincipalArn': self.get_principal_arn()
1274
1271
  }
@@ -0,0 +1,62 @@
1
+ # Implements generic Plugin class to load plugins
2
+
3
+ import json, yaml
4
+ from importlib import resources
5
+ import logging
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ class Plugin():
10
+
11
+ def __init__(self, name):
12
+ logger.debug(f'Initializing plugin {name}')
13
+ self.resources = {}
14
+ self.name = name
15
+ data_directory = 'data'
16
+ data_path = resources.files(self.name) / data_directory
17
+ if data_path.is_dir():
18
+ for resource_file in data_path.iterdir():
19
+ if resource_file.is_file():
20
+ logger.debug(f'Located data file: {resource_file.name}')
21
+ ext = resource_file.name.rsplit('.', -1)[-1].lower()
22
+ content = None
23
+ if ext == 'json':
24
+ content = json.loads(resource_file.read_text())
25
+ logger.debug(f'Loaded {resource_file.name} as JSON')
26
+ elif ext in ['yaml', 'yml']:
27
+ content = yaml.load(resource_file.read_text(), Loader=yaml.SafeLoader)
28
+ logger.debug(f'Loaded {resource_file.name} as YAML')
29
+ if content is None:
30
+ logger.info(f'Unsupported file type: {resource_file.name}')
31
+ continue
32
+ # If plugin has resources defined in different files,
33
+ # they will be merged into one dict
34
+ resource_kind = resource_file.name.rsplit('.', -1)[0]
35
+ supported_resource_kinds = ['dashboards', 'views', 'datasets']
36
+ if resource_kind in supported_resource_kinds:
37
+ self.resources[resource_kind] = content
38
+ logger.info(f'Loaded {resource_kind} from {resource_file.name}')
39
+ # If plugin has resources defined in one file,
40
+ # simply add it to resources dict
41
+ else:
42
+ self.resources.update(content)
43
+ # Add plugin name to every resource
44
+ for v in self.resources.values():
45
+ for item in v.values():
46
+ if item is not None:
47
+ item['providedBy'] = self.name
48
+ item.update({'source': str(resource_file)})
49
+ logger.debug(f'Plugin {self.name} initialized')
50
+
51
+ def provides(self) -> dict:
52
+ logger.debug(f'Provides: {self.resources}')
53
+ return self.resources
54
+
55
+ def get_resource(self, resource_name) -> str:
56
+ _resource_path = resources.files(self.name) / f'data/{resource_name}'
57
+ if _resource_path.is_file():
58
+ logger.info(f'Resource {resource_name} found')
59
+ _content = _resource_path.read_text()
60
+ logger.debug(f'Resource {resource_name} content: {_content}')
61
+ return _content
62
+ return None