From cf1de84f54cfc4ede1842fe0f5c0f3500f152e35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 06:21:14 -0500 Subject: [PATCH 01/14] Bump urllib3 from 2.0.3 to 2.0.6 in /Utilities/AzureOnboardingCLI (#90) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.3 to 2.0.6. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.0.3...2.0.6) --- updated-dependencies: - dependency-name: urllib3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Utilities/AzureOnboardingCLI/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/AzureOnboardingCLI/requirements.txt b/Utilities/AzureOnboardingCLI/requirements.txt index 9fcbdf3..eb77381 100644 --- a/Utilities/AzureOnboardingCLI/requirements.txt +++ b/Utilities/AzureOnboardingCLI/requirements.txt @@ -22,4 +22,4 @@ requests-oauthlib==1.3.1 six==1.16.0 spotinst-sdk2==2.1.38 typing_extensions==4.7.1 -urllib3==2.0.3 +urllib3==2.0.6 From 1e0b14f5ed0c3b6a747b0b1c205f95f774095a6a Mon Sep 17 00:00:00 2001 From: Dustin Venable <62815629+DusVen44@users.noreply.github.com> Date: Wed, 4 Oct 2023 10:03:50 -0500 Subject: [PATCH 02/14] Update Spot-AWS-Restricted-EKS.json (#91) Added 2 permissions for AccessEKS. DescribeNodegroup and ListNodegroups These 2 allow the new Spot Console Integration to auto-create VNGs for each EKS Cluster --- Policies/AWS/Spot-AWS-Restricted-EKS.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Policies/AWS/Spot-AWS-Restricted-EKS.json b/Policies/AWS/Spot-AWS-Restricted-EKS.json index 287607e..f2f6271 100644 --- a/Policies/AWS/Spot-AWS-Restricted-EKS.json +++ b/Policies/AWS/Spot-AWS-Restricted-EKS.json @@ -117,7 +117,9 @@ { "Sid": "AccessEks", "Action": [ - "eks:ListClusters" + "eks:ListClusters", + "eks:DescribeNodegroup", + "eks:ListNodegroups" ], "Effect": "Allow", "Resource": [ From a817ce8fcdc906c706bc201c5df8ae2e87d544b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 10:04:29 -0500 Subject: [PATCH 03/14] Bump cryptography from 41.0.3 to 41.0.4 in /Utilities/AzureOnboardingCLI (#89) Bumps [cryptography](https://github.com/pyca/cryptography) from 41.0.3 to 41.0.4. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/41.0.3...41.0.4) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Utilities/AzureOnboardingCLI/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/AzureOnboardingCLI/requirements.txt b/Utilities/AzureOnboardingCLI/requirements.txt index eb77381..bf90e8b 100644 --- a/Utilities/AzureOnboardingCLI/requirements.txt +++ b/Utilities/AzureOnboardingCLI/requirements.txt @@ -6,7 +6,7 @@ azure-mgmt-subscription==3.1.1 certifi==2023.7.22 cffi==1.15.1 charset-normalizer==3.2.0 -cryptography==41.0.3 +cryptography==41.0.4 idna==3.4 isodate==0.6.1 msal==1.22.0 From 5e5dca56936f8a3b96b8136c70326a2a7a202a09 Mon Sep 17 00:00:00 2001 From: Volodymyr Vozniak Date: Mon, 18 Dec 2023 15:42:41 -0500 Subject: [PATCH 04/14] Update SpotFinOps IAM Read Only role policy template (#94) --- ...finopsrole-stack-restricted-read-only.json | 162 +++++++++++--- ...finopsrole-stack-restricted-read-only.yaml | 211 ++++++++++++------ 2 files changed, 265 insertions(+), 108 deletions(-) diff --git a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only.json b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only.json index d79ba2f..81af189 100644 --- a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only.json +++ b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only.json @@ -28,11 +28,28 @@ "SpotFinOpsManagedPolicy": { "Type": "AWS::IAM::ManagedPolicy", "Properties": { - "ManagedPolicyName": { "Ref" : "PolicyName" }, + "ManagedPolicyName": { + "Ref": "PolicyName" + }, "Description": "Spot by NetApp Finops ReadOnly Policy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ + { + "Action": [ + "cloudformation:Describe*", + "cloudformation:EstimateTemplateCost", + "cloudformation:Get*", + "cloudformation:List*", + "cloudformation:ValidateTemplate", + "cloudformation:Detect*" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyCloudFormation" + }, { "Action": [ "es:ListElasticsearchInstanceTypes", @@ -49,7 +66,8 @@ "Action": [ "rds:DescribeReservedDBInstances", "rds:DescribeDBInstances", - "rds:DescribeReservedDBInstancesOfferings" + "rds:DescribeReservedDBInstancesOfferings", + "rds:ListTagsForResource" ], "Resource": [ "*" @@ -73,7 +91,9 @@ "Action": [ "elasticache:DescribeReservedCacheNodesOfferings", "elasticache:DescribeReservedCacheNodes", - "elasticache:DescribeCacheClusters" + "elasticache:DescribeCacheClusters", + "elasticache:ListAllowedNodeTypeModifications", + "elasticache:ListTagsForResource" ], "Resource": [ "*" @@ -105,8 +125,11 @@ }, { "Action": [ - "savingsplans:describe*", - "savingsplans:list*" + "savingsplans:DescribeSavingsPlanRates", + "savingsplans:DescribeSavingsPlans", + "savingsplans:DescribeSavingsPlansOfferingRates", + "savingsplans:DescribeSavingsPlansOfferings", + "savingsplans:ListTagsForResource" ], "Resource": [ "*" @@ -116,47 +139,90 @@ }, { "Action": [ - "aws-portal:ViewBilling", - "aws-portal:ViewUsage" + "account:GetAccountInformation", + "billing:GetBillingData", + "billing:GetBillingDetails", + "billing:GetBillingNotifications", + "billing:GetBillingPreferences", + "billing:GetContractInformation", + "billing:GetCredits", + "billing:GetIAMAccessPreference", + "billing:GetSellerOfRecord", + "billing:ListBillingViews", + "ce:DescribeNotificationSubscription", + "ce:DescribeReport", + "ce:GetAnomalies", + "ce:GetAnomalyMonitors", + "ce:GetAnomalySubscriptions", + "ce:GetCostAndUsage", + "ce:GetCostAndUsageWithResources", + "ce:GetCostCategories", + "ce:GetCostForecast", + "ce:GetDimensionValues", + "ce:GetPreferences", + "ce:GetReservationCoverage", + "ce:GetReservationPurchaseRecommendation", + "ce:GetReservationUtilization", + "ce:GetRightsizingRecommendation", + "ce:GetSavingsPlansCoverage", + "ce:GetSavingsPlansPurchaseRecommendation", + "ce:GetSavingsPlansUtilization", + "ce:GetSavingsPlansUtilizationDetails", + "ce:GetTags", + "ce:GetUsageForecast", + "ce:ListCostAllocationTags", + "ce:ListSavingsPlansPurchaseRecommendationGeneration", + "consolidatedbilling:GetAccountBillingRole", + "consolidatedbilling:ListLinkedAccounts", + "cur:GetClassicReport", + "cur:GetClassicReportPreferences", + "cur:ValidateReportDestination", + "freetier:GetFreeTierAlertPreference", + "freetier:GetFreeTierUsage", + "invoicing:GetInvoiceEmailDeliveryPreferences", + "invoicing:GetInvoicePDF", + "invoicing:ListInvoiceSummaries", + "payments:GetPaymentInstrument", + "payments:GetPaymentStatus", + "payments:ListPaymentPreferences", + "tax:GetTaxInheritance", + "tax:GetTaxRegistrationDocument", + "tax:ListTaxRegistrations" ], "Resource": [ "*" ], "Effect": "Allow", - "Sid": "BillingdeprecatedJuly6" + "Sid": "ReadOnlyViewBilling" }, { "Action": [ - "cur:DescribeReportDefinitions", - "ce:Get*", - "ce:Describe*", - "ce:List*", - "account:GetAccountInformation", - "billing:Get*", - "consolidatedbilling:Get*", - "consolidatedbilling:List*", - "invoicing:List*", - "invoicing:Get*", - "tax:ListTaxRegistrations", - "cur:Get*", - "cur:Validate*", - "freetier:Get*" + "cur:GetUsageReport", + "cur:DescribeReportDefinitions" ], "Resource": [ "*" ], "Effect": "Allow", - "Sid": "BillingPolicy" + "Sid": "ReadOnlyViewUsage" }, { "Action": [ "s3:GetBucketLocation", - "s3:ListBucketMultipartUploads", "s3:AbortMultipartUpload", + "s3:ListAccessPoints", + "s3:ListAccessPointsForObjectLambda", + "s3:ListAllMyBuckets", + "s3:ListBucket", + "s3:ListBucketMultipartUploads", + "s3:ListBucketVersions", + "s3:ListJobs", + "s3:ListMultiRegionAccessPoints", "s3:ListMultipartUploadParts", + "s3:ListStorageLensConfigurations", + "s3:ListStorageLensConfigurations", + "s3:ListTagsForResource", "s3:PutObject", - "s3:ListBucket", - "s3:List*", "s3:PutObjectTagging", "s3:PutObjectAcl" ], @@ -172,19 +238,40 @@ "s3:GetBucketLocation" ], "Resource": [ - { "Fn::Join" : [ "", [ "arn:aws:s3:::", { "Ref" : "CostAndUsageBucket" }]]} + { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "CostAndUsageBucket" + } + ] + ] + } ], "Effect": "Allow", "Sid": "S3CURBucket" }, { "Action": [ - "s3:get*", + "s3:Get*", "s3:List*", "s3:Describe*" ], "Resource": [ - { "Fn::Join" : [ "", [ "arn:aws:s3:::", { "Ref" : "CostAndUsageBucket" },"/*"]]} + { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "CostAndUsageBucket" + }, + "/*" + ] + ] + } ], "Effect": "Allow", "Sid": "S3CURObject" @@ -202,22 +289,25 @@ { "Action": "sts:AssumeRole", "Principal": { - "AWS": ["arn:aws:iam::884866656237:root", - "arn:aws:iam::627743545735:root"] + "AWS": [ + "arn:aws:iam::884866656237:root", + "arn:aws:iam::627743545735:root" + ] }, "Effect": "Allow" } ] }, - "Description" : "Spot by NetApp ReadOnly Finops IAM Role", + "Description": "Spot by NetApp ReadOnly Finops IAM Role", "ManagedPolicyArns": [ { "Ref": "SpotFinOpsManagedPolicy" - }, - "arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess" + } ], - "RoleName" : { "Ref" : "RoleName" } + "RoleName": { + "Ref": "RoleName" + } } } } -} +} \ No newline at end of file diff --git a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only.yaml b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only.yaml index 9a5c87a..f2d1d4e 100644 --- a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only.yaml +++ b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only.yaml @@ -1,11 +1,16 @@ AWSTemplateFormatVersion: '2010-09-09' Outputs: SpotFinOpsRoleArn: - Value: !GetAtt 'SpotFinOpsRole.Arn' + Value: + 'Fn::GetAtt': + - SpotFinOpsRole + - Arn Parameters: CostAndUsageBucket: Type: String - Description: The bucket name of where the *HOURLY* Cost and Usage Report is located. https://console.aws.amazon.com/billing/home?#/reports + Description: >- + The bucket name of where the *HOURLY* Cost and Usage Report is located. + https://console.aws.amazon.com/billing/home?#/reports RoleName: Type: String Default: SpotByNetApp_Finops_ReadOnly @@ -14,142 +19,204 @@ Parameters: Default: SpotByNetApp_Finops_ReadOnly_Policy Resources: SpotFinOpsManagedPolicy: - Type: AWS::IAM::ManagedPolicy + Type: 'AWS::IAM::ManagedPolicy' Properties: - ManagedPolicyName: !Ref 'PolicyName' + ManagedPolicyName: + Ref: PolicyName Description: Spot by NetApp Finops ReadOnly Policy PolicyDocument: Version: '2012-10-17' Statement: - Action: - - es:ListElasticsearchInstanceTypes - - es:DescribeReservedElasticsearchInstanceOfferings - - es:DescribeReservedElasticsearchInstances + - 'cloudformation:Describe*' + - 'cloudformation:EstimateTemplateCost' + - 'cloudformation:Get*' + - 'cloudformation:List*' + - 'cloudformation:ValidateTemplate' + - 'cloudformation:Detect*' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyCloudFormation + - Action: + - 'es:ListElasticsearchInstanceTypes' + - 'es:DescribeReservedElasticsearchInstanceOfferings' + - 'es:DescribeReservedElasticsearchInstances' Resource: - '*' Effect: Allow Sid: ReadOnlyElasticSearch - Action: - - rds:DescribeReservedDBInstances - - rds:DescribeDBInstances - - rds:DescribeReservedDBInstancesOfferings + - 'rds:DescribeReservedDBInstances' + - 'rds:DescribeDBInstances' + - 'rds:DescribeReservedDBInstancesOfferings' + - 'rds:ListTagsForResource' Resource: - '*' Effect: Allow Sid: ReadOnlyRDS - Action: - - redshift:DescribeReservedNodeOfferings - - redshift:DescribeReservedNodes - - redshift:DescribeClusters + - 'redshift:DescribeReservedNodeOfferings' + - 'redshift:DescribeReservedNodes' + - 'redshift:DescribeClusters' Resource: - '*' Effect: Allow Sid: ReadOnlyRedshift - Action: - - elasticache:DescribeReservedCacheNodesOfferings - - elasticache:DescribeReservedCacheNodes - - elasticache:DescribeCacheClusters + - 'elasticache:DescribeReservedCacheNodesOfferings' + - 'elasticache:DescribeReservedCacheNodes' + - 'elasticache:DescribeCacheClusters' + - 'elasticache:ListAllowedNodeTypeModifications' + - 'elasticache:ListTagsForResource' Resource: - '*' Effect: Allow Sid: ReadOnlyElasticache - Action: - - dynamodb:DescribeReservedCapacityOfferings - - dynamodb:DescribeReservedCapacity + - 'dynamodb:DescribeReservedCapacityOfferings' + - 'dynamodb:DescribeReservedCapacity' Resource: - '*' Effect: Allow Sid: ReadOnlyDynamoDB - Action: - - ec2:DescribeHostReservations - - ec2:DescribeReservedInstances + - 'ec2:DescribeHostReservations' + - 'ec2:DescribeReservedInstances' Resource: - '*' Effect: Allow Sid: ReadOnlyEC2 - Action: - - savingsplans:describe* - - savingsplans:list* + - 'savingsplans:DescribeSavingsPlanRates' + - 'savingsplans:DescribeSavingsPlans' + - 'savingsplans:DescribeSavingsPlansOfferingRates' + - 'savingsplans:DescribeSavingsPlansOfferings' + - 'savingsplans:ListTagsForResource' Resource: - '*' Effect: Allow Sid: ReadOnlySavingsPlans - Action: - - aws-portal:ViewBilling - - aws-portal:ViewUsage + - 'account:GetAccountInformation' + - 'billing:GetBillingData' + - 'billing:GetBillingDetails' + - 'billing:GetBillingNotifications' + - 'billing:GetBillingPreferences' + - 'billing:GetContractInformation' + - 'billing:GetCredits' + - 'billing:GetIAMAccessPreference' + - 'billing:GetSellerOfRecord' + - 'billing:ListBillingViews' + - 'ce:DescribeNotificationSubscription' + - 'ce:DescribeReport' + - 'ce:GetAnomalies' + - 'ce:GetAnomalyMonitors' + - 'ce:GetAnomalySubscriptions' + - 'ce:GetCostAndUsage' + - 'ce:GetCostAndUsageWithResources' + - 'ce:GetCostCategories' + - 'ce:GetCostForecast' + - 'ce:GetDimensionValues' + - 'ce:GetPreferences' + - 'ce:GetReservationCoverage' + - 'ce:GetReservationPurchaseRecommendation' + - 'ce:GetReservationUtilization' + - 'ce:GetRightsizingRecommendation' + - 'ce:GetSavingsPlansCoverage' + - 'ce:GetSavingsPlansPurchaseRecommendation' + - 'ce:GetSavingsPlansUtilization' + - 'ce:GetSavingsPlansUtilizationDetails' + - 'ce:GetTags' + - 'ce:GetUsageForecast' + - 'ce:ListCostAllocationTags' + - 'ce:ListSavingsPlansPurchaseRecommendationGeneration' + - 'consolidatedbilling:GetAccountBillingRole' + - 'consolidatedbilling:ListLinkedAccounts' + - 'cur:GetClassicReport' + - 'cur:GetClassicReportPreferences' + - 'cur:ValidateReportDestination' + - 'freetier:GetFreeTierAlertPreference' + - 'freetier:GetFreeTierUsage' + - 'invoicing:GetInvoiceEmailDeliveryPreferences' + - 'invoicing:GetInvoicePDF' + - 'invoicing:ListInvoiceSummaries' + - 'payments:GetPaymentInstrument' + - 'payments:GetPaymentStatus' + - 'payments:ListPaymentPreferences' + - 'tax:GetTaxInheritance' + - 'tax:GetTaxRegistrationDocument' + - 'tax:ListTaxRegistrations' Resource: - '*' Effect: Allow - Sid: BillingdeprecatedJuly6 + Sid: ReadOnlyViewBilling - Action: - - cur:DescribeReportDefinitions - - ce:Get* - - ce:Describe* - - ce:List* - - account:GetAccountInformation - - billing:Get* - - consolidatedbilling:Get* - - consolidatedbilling:List* - - invoicing:List* - - invoicing:Get* - - cur:Get* - - cur:Validate* - - freetier:Get* + - 'cur:GetUsageReport' + - 'cur:DescribeReportDefinitions' Resource: - '*' Effect: Allow - Sid: BillingPolicy + Sid: ReadOnlyViewUsage - Action: - - s3:GetBucketLocation - - s3:ListBucketMultipartUploads - - s3:AbortMultipartUpload - - s3:ListMultipartUploadParts - - s3:PutObject - - s3:ListBucket - - s3:List* - - s3:PutObjectTagging - - s3:PutObjectAcl - Resource: arn:aws:s3:::sc-customer-* + - 's3:GetBucketLocation' + - 's3:AbortMultipartUpload' + - 's3:ListAccessPoints' + - 's3:ListAccessPointsForObjectLambda' + - 's3:ListAllMyBuckets' + - 's3:ListBucket' + - 's3:ListBucketMultipartUploads' + - 's3:ListBucketVersions' + - 's3:ListJobs' + - 's3:ListMultiRegionAccessPoints' + - 's3:ListMultipartUploadParts' + - 's3:ListStorageLensConfigurations' + - 's3:ListStorageLensConfigurations' + - 's3:ListTagsForResource' + - 's3:PutObject' + - 's3:PutObjectTagging' + - 's3:PutObjectAcl' + Resource: 'arn:aws:s3:::sc-customer-*' Effect: Allow Sid: S3SyncPermissions - Action: - - s3:ListBucket - - s3:ListBucketVersions - - s3:ListBucketMultipartUploads - - s3:GetBucketLocation + - 's3:ListBucket' + - 's3:ListBucketVersions' + - 's3:ListBucketMultipartUploads' + - 's3:GetBucketLocation' Resource: - - !Join - - '' - - - 'arn:aws:s3:::' - - !Ref 'CostAndUsageBucket' + - 'Fn::Join': + - '' + - - 'arn:aws:s3:::' + - Ref: CostAndUsageBucket Effect: Allow Sid: S3CURBucket - Action: - - s3:get* - - s3:List* - - s3:Describe* + - 's3:Get*' + - 's3:List*' + - 's3:Describe*' Resource: - - !Join - - '' - - - 'arn:aws:s3:::' - - !Ref 'CostAndUsageBucket' - - /* + - 'Fn::Join': + - '' + - - 'arn:aws:s3:::' + - Ref: CostAndUsageBucket + - /* Effect: Allow Sid: S3CURObject SpotFinOpsRole: - Type: AWS::IAM::Role + Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - - Action: sts:AssumeRole + - Action: 'sts:AssumeRole' Principal: AWS: - - arn:aws:iam::884866656237:root - - arn:aws:iam::627743545735:root + - 'arn:aws:iam::884866656237:root' + - 'arn:aws:iam::627743545735:root' Effect: Allow Description: Spot by NetApp ReadOnly Finops IAM Role ManagedPolicyArns: - - !Ref 'SpotFinOpsManagedPolicy' - - arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess - RoleName: !Ref 'RoleName' + - Ref: SpotFinOpsManagedPolicy + RoleName: + Ref: RoleName From c0ff8640f6b111f162d349e7f2992ba1ce0014b5 Mon Sep 17 00:00:00 2001 From: Volodymyr Vozniak Date: Thu, 11 Jan 2024 12:14:17 -0500 Subject: [PATCH 05/14] spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket template updated (#96) --- ...ly-limited-assume-with-cur-and-bucket.json | 722 ++++++++++-------- ...ly-limited-assume-with-cur-and-bucket.yaml | 288 +++++++ 2 files changed, 681 insertions(+), 329 deletions(-) create mode 100644 CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.yaml diff --git a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.json b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.json index 7344bdd..44bef74 100644 --- a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.json +++ b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.json @@ -1,354 +1,418 @@ { "AWSTemplateFormatVersion": "2010-09-09", "Outputs": { - "SpotFinOpsRoleArn": { - "Value": { - "Fn::GetAtt": [ - "SpotFinOpsRole", - "Arn" - ] - } + "SpotFinOpsRoleArn": { + "Value": { + "Fn::GetAtt": [ + "SpotFinOpsRole", + "Arn" + ] } + } }, "Parameters": { - "CostAndUsageBucket": { - "Type": "String", - "Description": "The bucket name of where the *HOURLY* Cost and Usage Report is located. https://console.aws.amazon.com/billing/home?#/reports" - }, - "RoleName": { - "Type": "String", - "Default": "SpotByNetApp_Finops_ReadOnly" - }, - "PolicyName": { - "Type": "String", - "Default": "SpotByNetApp_Finops_ReadOnly_Policy" - } + "CostAndUsageBucket": { + "Type": "String", + "Description": "The bucket name of where the *HOURLY* Cost and Usage Report is located. https://console.aws.amazon.com/billing/home?#/reports" + }, + "RoleName": { + "Type": "String", + "Default": "SpotByNetApp_Finops_ReadOnly" + }, + "PolicyName": { + "Type": "String", + "Default": "SpotByNetApp_Finops_ReadOnly_Policy" + } }, "Resources": { - "SpotCurBucket": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketName": { - "Fn::Sub": [ - "${CostAndUsageBucket}${RandomGUID}", + "SpotCurBucket": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": { + "Fn::Sub": [ + "${CostAndUsageBucket}${RandomGUID}", + { + "RandomGUID": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", { - "RandomGUID": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "-", - { - "Fn::Select": [ - 2, - { - "Fn::Split": [ - "/", - { - "Ref": "AWS::StackId" - } - ] - } - ] - } - ] - } - ] + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] } + ] } - ] + ] + } + ] } - } - }, - "CostAndUsageReport": { - "Type": "AWS::CUR::ReportDefinition", - "Properties": { - "ReportName": "spot-io-cur", - "TimeUnit": "HOURLY", - "Format": "Parquet", - "Compression": "Parquet", - "S3Bucket": { - "Ref": "CostAndUsageBucket" + } + ] + } + } + }, + "CostAndUsageReport": { + "Type": "AWS::CUR::ReportDefinition", + "Properties": { + "ReportName": "spot-io-cur", + "TimeUnit": "HOURLY", + "Format": "Parquet", + "Compression": "Parquet", + "S3Bucket": { + "Ref": "CostAndUsageBucket" + }, + "S3Prefix": "cost-and-usage-reports/", + "S3Region": "us-east-1", + "AdditionalSchemaElements": [ + "RESOURCES" + ], + "AdditionalArtifacts": [ + "ATHENA" + ], + "RefreshClosedReports": true, + "ReportVersioning": "OVERWRITE_REPORT" + } + }, + "S3BucketPolicy": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "CostAndUsageBucket" + }, + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowCostAndUsageReportPut", + "Effect": "Allow", + "Principal": { + "Service": [ + "billingreports.amazonaws.com" + ] }, - "S3Prefix": "cost-and-usage-reports/", - "S3Region": "us-east-1", - "AdditionalSchemaElements": [ - "RESOURCES" + "Action": [ + "s3:PutObject" ], - "AdditionalArtifacts": [ - "ATHENA" - ], - "RefreshClosedReports": true, - "ReportVersioning": "OVERWRITE_REPORT" - } - }, - "S3BucketPolicy": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "CostAndUsageBucket" - }, - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "AllowCostAndUsageReportPut", - "Effect": "Allow", - "Principal": { - "Service": [ - "billingreports.amazonaws.com" - ] - }, - "Action": [ - "s3:PutObject" - ], - "Resource": { - "Fn::Sub": "arn:aws:s3:::${CostAndUsageBucket}/*" - } - }, - { - "Sid": "AllowCostAndUsageReport", - "Effect": "Allow", - "Principal": { - "Service": [ - "billingreports.amazonaws.com" - ] - }, - "Action": [ - "s3:GetBucketAcl", - "s3:GetBucketPolicy" - ], - "Resource": { - "Fn::Sub": "arn:aws:s3:::${CostAndUsageBucket}" - } - } - ] + "Resource": { + "Fn::Sub": "arn:aws:s3:::${CostAndUsageBucket}/*" } - } - }, - "SpotFinOpsManagedPolicy": { - "Type": "AWS::IAM::ManagedPolicy", - "Properties": { - "ManagedPolicyName": { - "Ref": "PolicyName" + }, + { + "Sid": "AllowCostAndUsageReport", + "Effect": "Allow", + "Principal": { + "Service": [ + "billingreports.amazonaws.com" + ] }, - "Description": "Spot by NetApp Finops ReadOnly Policy", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "es:ListElasticsearchInstanceTypes", - "es:DescribeReservedElasticsearchInstanceOfferings", - "es:DescribeReservedElasticsearchInstances" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyElasticSearch" - }, - { - "Action": [ - "rds:DescribeReservedDBInstances", - "rds:DescribeDBInstances", - "rds:DescribeReservedDBInstancesOfferings" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyRDS" - }, - { - "Action": [ - "redshift:DescribeReservedNodeOfferings", - "redshift:DescribeReservedNodes", - "redshift:DescribeClusters" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyRedshift" - }, - { - "Action": [ - "elasticache:DescribeReservedCacheNodesOfferings", - "elasticache:DescribeReservedCacheNodes", - "elasticache:DescribeCacheClusters" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyElasticache" - }, - { - "Action": [ - "dynamodb:DescribeReservedCapacityOfferings", - "dynamodb:DescribeReservedCapacity" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyDynamoDB" - }, - { - "Action": [ - "ec2:DescribeHostReservations", - "ec2:DescribeReservedInstances" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyEC2" - }, - { - "Action": [ - "savingsplans:describe*", - "savingsplans:list*" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlySavingsPlans" - }, - { - "Action": [ - "aws-portal:ViewBilling", - "aws-portal:ViewUsage" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "BillingdeprecatedJuly6" - }, - { - "Action": [ - "cur:DescribeReportDefinitions", - "ce:Get*", - "ce:Describe*", - "ce:List*", - "account:GetAccountInformation", - "billing:Get*", - "consolidatedbilling:Get*", - "consolidatedbilling:List*", - "invoicing:List*", - "invoicing:Get*", - "cur:Get*", - "cur:Validate*", - "freetier:Get*" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "BillingPolicy" - }, - { - "Action": [ - "s3:GetBucketLocation", - "s3:ListBucketMultipartUploads", - "s3:AbortMultipartUpload", - "s3:ListMultipartUploadParts", - "s3:PutObject", - "s3:ListBucket", - "s3:List*", - "s3:PutObjectTagging", - "s3:PutObjectAcl" - ], - "Resource": "arn:aws:s3:::sc-customer-*", - "Effect": "Allow", - "Sid": "S3SyncPermissions" - }, - { - "Action": [ - "s3:ListBucket", - "s3:ListBucketVersions", - "s3:ListBucketMultipartUploads", - "s3:GetBucketLocation" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - { - "Ref": "CostAndUsageBucket" - } - ] - ] - } - ], - "Effect": "Allow", - "Sid": "S3CURBucket" - }, + "Action": [ + "s3:GetBucketAcl", + "s3:GetBucketPolicy" + ], + "Resource": { + "Fn::Sub": "arn:aws:s3:::${CostAndUsageBucket}" + } + } + ] + } + } + }, + "SpotFinOpsManagedPolicy": { + "Type": "AWS::IAM::ManagedPolicy", + "Properties": { + "ManagedPolicyName": { + "Ref": "PolicyName" + }, + "Description": "Spot by NetApp Finops ReadOnly Policy", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "cloudformation:Describe*", + "cloudformation:EstimateTemplateCost", + "cloudformation:Get*", + "cloudformation:List*", + "cloudformation:ValidateTemplate", + "cloudformation:Detect*" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyCloudFormation" + }, + { + "Action": [ + "es:ListElasticsearchInstanceTypes", + "es:DescribeReservedElasticsearchInstanceOfferings", + "es:DescribeReservedElasticsearchInstances" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyElasticSearch" + }, + { + "Action": [ + "rds:DescribeReservedDBInstances", + "rds:DescribeDBInstances", + "rds:DescribeReservedDBInstancesOfferings", + "rds:ListTagsForResource" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyRDS" + }, + { + "Action": [ + "redshift:DescribeReservedNodeOfferings", + "redshift:DescribeReservedNodes", + "redshift:DescribeClusters" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyRedshift" + }, + { + "Action": [ + "elasticache:DescribeReservedCacheNodesOfferings", + "elasticache:DescribeReservedCacheNodes", + "elasticache:DescribeCacheClusters", + "elasticache:ListAllowedNodeTypeModifications", + "elasticache:ListTagsForResource" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyElasticache" + }, + { + "Action": [ + "dynamodb:DescribeReservedCapacityOfferings", + "dynamodb:DescribeReservedCapacity" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyDynamoDB" + }, + { + "Action": [ + "ec2:DescribeHostReservations", + "ec2:DescribeReservedInstances" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyEC2" + }, + { + "Action": [ + "savingsplans:DescribeSavingsPlanRates", + "savingsplans:DescribeSavingsPlans", + "savingsplans:DescribeSavingsPlansOfferingRates", + "savingsplans:DescribeSavingsPlansOfferings", + "savingsplans:ListTagsForResource" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlySavingsPlans" + }, + { + "Action": [ + "account:GetAccountInformation", + "billing:GetBillingData", + "billing:GetBillingDetails", + "billing:GetBillingNotifications", + "billing:GetBillingPreferences", + "billing:GetContractInformation", + "billing:GetCredits", + "billing:GetIAMAccessPreference", + "billing:GetSellerOfRecord", + "billing:ListBillingViews", + "ce:DescribeNotificationSubscription", + "ce:DescribeReport", + "ce:GetAnomalies", + "ce:GetAnomalyMonitors", + "ce:GetAnomalySubscriptions", + "ce:GetCostAndUsage", + "ce:GetCostAndUsageWithResources", + "ce:GetCostCategories", + "ce:GetCostForecast", + "ce:GetDimensionValues", + "ce:GetPreferences", + "ce:GetReservationCoverage", + "ce:GetReservationPurchaseRecommendation", + "ce:GetReservationUtilization", + "ce:GetRightsizingRecommendation", + "ce:GetSavingsPlansCoverage", + "ce:GetSavingsPlansPurchaseRecommendation", + "ce:GetSavingsPlansUtilization", + "ce:GetSavingsPlansUtilizationDetails", + "ce:GetTags", + "ce:GetUsageForecast", + "ce:ListCostAllocationTags", + "ce:ListSavingsPlansPurchaseRecommendationGeneration", + "consolidatedbilling:GetAccountBillingRole", + "consolidatedbilling:ListLinkedAccounts", + "cur:GetClassicReport", + "cur:GetClassicReportPreferences", + "cur:ValidateReportDestination", + "freetier:GetFreeTierAlertPreference", + "freetier:GetFreeTierUsage", + "invoicing:GetInvoiceEmailDeliveryPreferences", + "invoicing:GetInvoicePDF", + "invoicing:ListInvoiceSummaries", + "payments:GetPaymentInstrument", + "payments:GetPaymentStatus", + "payments:ListPaymentPreferences", + "tax:GetTaxInheritance", + "tax:GetTaxRegistrationDocument", + "tax:ListTaxRegistrations" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyViewBilling" + }, + { + "Action": [ + "cur:GetUsageReport", + "cur:DescribeReportDefinitions" + ], + "Resource": [ + "*" + ], + "Effect": "Allow", + "Sid": "ReadOnlyViewUsage" + }, + { + "Action": [ + "s3:GetBucketLocation", + "s3:AbortMultipartUpload", + "s3:ListAccessPoints", + "s3:ListAccessPointsForObjectLambda", + "s3:ListAllMyBuckets", + "s3:ListBucket", + "s3:ListBucketMultipartUploads", + "s3:ListBucketVersions", + "s3:ListJobs", + "s3:ListMultiRegionAccessPoints", + "s3:ListMultipartUploadParts", + "s3:ListStorageLensConfigurations", + "s3:ListStorageLensConfigurations", + "s3:ListTagsForResource", + "s3:PutObject", + "s3:PutObjectTagging", + "s3:PutObjectAcl" + ], + "Resource": "arn:aws:s3:::sc-customer-*", + "Effect": "Allow", + "Sid": "S3SyncPermissions" + }, + { + "Action": [ + "s3:ListBucket", + "s3:ListBucketVersions", + "s3:ListBucketMultipartUploads", + "s3:GetBucketLocation" + ], + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", { - "Action": [ - "s3:get*", - "s3:List*", - "s3:Describe*" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - { - "Ref": "CostAndUsageBucket" - }, - "/*" - ] - ] - } - ], - "Effect": "Allow", - "Sid": "S3CURObject" + "Ref": "CostAndUsageBucket" } + ] ] - } - } - }, - "SpotFinOpsRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ + } + ], + "Effect": "Allow", + "Sid": "S3CURBucket" + }, + { + "Action": [ + "s3:Get*", + "s3:List*", + "s3:Describe*" + ], + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", { - "Action": "sts:AssumeRole", - "Principal": { - "AWS": [ - "arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole", - "arn:aws:iam::884866656237:role/Admin", - "arn:aws:iam::884866656237:role/DevRole", - "arn:aws:iam::884866656237:role/ReadOnly", - "arn:aws:iam::627743545735:role/Admin", - "arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole" - ] - }, - "Effect": "Allow" - } + "Ref": "CostAndUsageBucket" + }, + "/*" + ] ] - }, - "Description": "Spot by NetApp ReadOnly Finops IAM Role", - "ManagedPolicyArns": [ - { - "Ref": "SpotFinOpsManagedPolicy" - }, - "arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess" + } ], - "RoleName": { - "Ref": "RoleName" - } + "Effect": "Allow", + "Sid": "S3CURObject" + } + ] + } + } + }, + "SpotFinOpsRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "AWS": [ + "arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole", + "arn:aws:iam::884866656237:role/Admin", + "arn:aws:iam::884866656237:role/DevRole", + "arn:aws:iam::884866656237:role/ReadOnly", + "arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole", + "arn:aws:iam::627743545735:role/Admin" + ] + }, + "Effect": "Allow" + } + ] + }, + "Description": "Spot by NetApp ReadOnly Finops IAM Role", + "ManagedPolicyArns": [ + { + "Ref": "SpotFinOpsManagedPolicy" } + ], + "RoleName": { + "Ref": "RoleName" + } } + } } -} \ No newline at end of file + } \ No newline at end of file diff --git a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.yaml b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.yaml new file mode 100644 index 0000000..0459fa4 --- /dev/null +++ b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.yaml @@ -0,0 +1,288 @@ +AWSTemplateFormatVersion: '2010-09-09' +Outputs: + SpotFinOpsRoleArn: + Value: + 'Fn::GetAtt': + - SpotFinOpsRole + - Arn +Parameters: + CostAndUsageBucket: + Type: String + Description: >- + The bucket name of where the *HOURLY* Cost and Usage Report is located. + https://console.aws.amazon.com/billing/home?#/reports + RoleName: + Type: String + Default: SpotByNetApp_Finops_ReadOnly + PolicyName: + Type: String + Default: SpotByNetApp_Finops_ReadOnly_Policy +Resources: + SpotCurBucket: + Type: 'AWS::S3::Bucket' + Properties: + BucketName: + 'Fn::Sub': + - '${CostAndUsageBucket}${RandomGUID}' + - RandomGUID: + 'Fn::Select': + - 0 + - 'Fn::Split': + - '-' + - 'Fn::Select': + - 2 + - 'Fn::Split': + - / + - Ref: 'AWS::StackId' + CostAndUsageReport: + Type: 'AWS::CUR::ReportDefinition' + Properties: + ReportName: spot-io-cur + TimeUnit: HOURLY + Format: Parquet + Compression: Parquet + S3Bucket: + Ref: CostAndUsageBucket + S3Prefix: cost-and-usage-reports/ + S3Region: us-east-1 + AdditionalSchemaElements: + - RESOURCES + AdditionalArtifacts: + - ATHENA + RefreshClosedReports: true + ReportVersioning: OVERWRITE_REPORT + S3BucketPolicy: + Type: 'AWS::S3::BucketPolicy' + Properties: + Bucket: + Ref: CostAndUsageBucket + PolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: AllowCostAndUsageReportPut + Effect: Allow + Principal: + Service: + - billingreports.amazonaws.com + Action: + - 's3:PutObject' + Resource: + 'Fn::Sub': 'arn:aws:s3:::${CostAndUsageBucket}/*' + - Sid: AllowCostAndUsageReport + Effect: Allow + Principal: + Service: + - billingreports.amazonaws.com + Action: + - 's3:GetBucketAcl' + - 's3:GetBucketPolicy' + Resource: + 'Fn::Sub': 'arn:aws:s3:::${CostAndUsageBucket}' + SpotFinOpsManagedPolicy: + Type: 'AWS::IAM::ManagedPolicy' + Properties: + ManagedPolicyName: + Ref: PolicyName + Description: Spot by NetApp Finops ReadOnly Policy + PolicyDocument: + Version: '2012-10-17' + Statement: + - Action: + - 'cloudformation:Describe*' + - 'cloudformation:EstimateTemplateCost' + - 'cloudformation:Get*' + - 'cloudformation:List*' + - 'cloudformation:ValidateTemplate' + - 'cloudformation:Detect*' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyCloudFormation + - Action: + - 'es:ListElasticsearchInstanceTypes' + - 'es:DescribeReservedElasticsearchInstanceOfferings' + - 'es:DescribeReservedElasticsearchInstances' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyElasticSearch + - Action: + - 'rds:DescribeReservedDBInstances' + - 'rds:DescribeDBInstances' + - 'rds:DescribeReservedDBInstancesOfferings' + - 'rds:ListTagsForResource' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyRDS + - Action: + - 'redshift:DescribeReservedNodeOfferings' + - 'redshift:DescribeReservedNodes' + - 'redshift:DescribeClusters' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyRedshift + - Action: + - 'elasticache:DescribeReservedCacheNodesOfferings' + - 'elasticache:DescribeReservedCacheNodes' + - 'elasticache:DescribeCacheClusters' + - 'elasticache:ListAllowedNodeTypeModifications' + - 'elasticache:ListTagsForResource' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyElasticache + - Action: + - 'dynamodb:DescribeReservedCapacityOfferings' + - 'dynamodb:DescribeReservedCapacity' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyDynamoDB + - Action: + - 'ec2:DescribeHostReservations' + - 'ec2:DescribeReservedInstances' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyEC2 + - Action: + - 'savingsplans:DescribeSavingsPlanRates' + - 'savingsplans:DescribeSavingsPlans' + - 'savingsplans:DescribeSavingsPlansOfferingRates' + - 'savingsplans:DescribeSavingsPlansOfferings' + - 'savingsplans:ListTagsForResource' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlySavingsPlans + - Action: + - 'account:GetAccountInformation' + - 'billing:GetBillingData' + - 'billing:GetBillingDetails' + - 'billing:GetBillingNotifications' + - 'billing:GetBillingPreferences' + - 'billing:GetContractInformation' + - 'billing:GetCredits' + - 'billing:GetIAMAccessPreference' + - 'billing:GetSellerOfRecord' + - 'billing:ListBillingViews' + - 'ce:DescribeNotificationSubscription' + - 'ce:DescribeReport' + - 'ce:GetAnomalies' + - 'ce:GetAnomalyMonitors' + - 'ce:GetAnomalySubscriptions' + - 'ce:GetCostAndUsage' + - 'ce:GetCostAndUsageWithResources' + - 'ce:GetCostCategories' + - 'ce:GetCostForecast' + - 'ce:GetDimensionValues' + - 'ce:GetPreferences' + - 'ce:GetReservationCoverage' + - 'ce:GetReservationPurchaseRecommendation' + - 'ce:GetReservationUtilization' + - 'ce:GetRightsizingRecommendation' + - 'ce:GetSavingsPlansCoverage' + - 'ce:GetSavingsPlansPurchaseRecommendation' + - 'ce:GetSavingsPlansUtilization' + - 'ce:GetSavingsPlansUtilizationDetails' + - 'ce:GetTags' + - 'ce:GetUsageForecast' + - 'ce:ListCostAllocationTags' + - 'ce:ListSavingsPlansPurchaseRecommendationGeneration' + - 'consolidatedbilling:GetAccountBillingRole' + - 'consolidatedbilling:ListLinkedAccounts' + - 'cur:GetClassicReport' + - 'cur:GetClassicReportPreferences' + - 'cur:ValidateReportDestination' + - 'freetier:GetFreeTierAlertPreference' + - 'freetier:GetFreeTierUsage' + - 'invoicing:GetInvoiceEmailDeliveryPreferences' + - 'invoicing:GetInvoicePDF' + - 'invoicing:ListInvoiceSummaries' + - 'payments:GetPaymentInstrument' + - 'payments:GetPaymentStatus' + - 'payments:ListPaymentPreferences' + - 'tax:GetTaxInheritance' + - 'tax:GetTaxRegistrationDocument' + - 'tax:ListTaxRegistrations' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyViewBilling + - Action: + - 'cur:GetUsageReport' + - 'cur:DescribeReportDefinitions' + Resource: + - '*' + Effect: Allow + Sid: ReadOnlyViewUsage + - Action: + - 's3:GetBucketLocation' + - 's3:AbortMultipartUpload' + - 's3:ListAccessPoints' + - 's3:ListAccessPointsForObjectLambda' + - 's3:ListAllMyBuckets' + - 's3:ListBucket' + - 's3:ListBucketMultipartUploads' + - 's3:ListBucketVersions' + - 's3:ListJobs' + - 's3:ListMultiRegionAccessPoints' + - 's3:ListMultipartUploadParts' + - 's3:ListStorageLensConfigurations' + - 's3:ListStorageLensConfigurations' + - 's3:ListTagsForResource' + - 's3:PutObject' + - 's3:PutObjectTagging' + - 's3:PutObjectAcl' + Resource: 'arn:aws:s3:::sc-customer-*' + Effect: Allow + Sid: S3SyncPermissions + - Action: + - 's3:ListBucket' + - 's3:ListBucketVersions' + - 's3:ListBucketMultipartUploads' + - 's3:GetBucketLocation' + Resource: + - 'Fn::Join': + - '' + - - 'arn:aws:s3:::' + - Ref: CostAndUsageBucket + Effect: Allow + Sid: S3CURBucket + - Action: + - 's3:Get*' + - 's3:List*' + - 's3:Describe*' + Resource: + - 'Fn::Join': + - '' + - - 'arn:aws:s3:::' + - Ref: CostAndUsageBucket + - /* + Effect: Allow + Sid: S3CURObject + SpotFinOpsRole: + Type: 'AWS::IAM::Role' + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Action: 'sts:AssumeRole' + Principal: + AWS: + - >- + arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole + - 'arn:aws:iam::884866656237:role/Admin' + - 'arn:aws:iam::884866656237:role/DevRole' + - 'arn:aws:iam::884866656237:role/ReadOnly' + - >- + arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole + - 'arn:aws:iam::627743545735:role/Admin' + Effect: Allow + Description: Spot by NetApp ReadOnly Finops IAM Role + ManagedPolicyArns: + - Ref: SpotFinOpsManagedPolicy + RoleName: + Ref: RoleName From bb857f53fcf7e1a86debbaeacb1d384f1f203528 Mon Sep 17 00:00:00 2001 From: Jeff Snyder Date: Wed, 13 Mar 2024 12:56:36 -0400 Subject: [PATCH 06/14] [src] SF-4348: Add external ID to read only role, deprecate old templates (#97) --- ...ly-limited-assume-with-cur-and-bucket.json | 418 ------------------ ...d-only-limited-assume-with-externalid.json | 242 ---------- ...k-restricted-read-only-limited-assume.json | 226 ---------- ...finopsrole-stack-restricted-read-only.json | 12 +- ...ly-limited-assume-with-cur-and-bucket.yaml | 288 ------------ ...d-only-limited-assume-with-externalid.yaml | 168 ------- ...k-restricted-read-only-limited-assume.yaml | 159 ------- ...finopsrole-stack-restricted-read-only.yaml | 5 + 8 files changed, 16 insertions(+), 1502 deletions(-) delete mode 100644 CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.json delete mode 100644 CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-externalid.json delete mode 100644 CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume.json delete mode 100644 CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.yaml delete mode 100644 CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-externalid.yaml delete mode 100644 CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume.yaml diff --git a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.json b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.json deleted file mode 100644 index 44bef74..0000000 --- a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.json +++ /dev/null @@ -1,418 +0,0 @@ -{ - "AWSTemplateFormatVersion": "2010-09-09", - "Outputs": { - "SpotFinOpsRoleArn": { - "Value": { - "Fn::GetAtt": [ - "SpotFinOpsRole", - "Arn" - ] - } - } - }, - "Parameters": { - "CostAndUsageBucket": { - "Type": "String", - "Description": "The bucket name of where the *HOURLY* Cost and Usage Report is located. https://console.aws.amazon.com/billing/home?#/reports" - }, - "RoleName": { - "Type": "String", - "Default": "SpotByNetApp_Finops_ReadOnly" - }, - "PolicyName": { - "Type": "String", - "Default": "SpotByNetApp_Finops_ReadOnly_Policy" - } - }, - "Resources": { - "SpotCurBucket": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketName": { - "Fn::Sub": [ - "${CostAndUsageBucket}${RandomGUID}", - { - "RandomGUID": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "-", - { - "Fn::Select": [ - 2, - { - "Fn::Split": [ - "/", - { - "Ref": "AWS::StackId" - } - ] - } - ] - } - ] - } - ] - } - } - ] - } - } - }, - "CostAndUsageReport": { - "Type": "AWS::CUR::ReportDefinition", - "Properties": { - "ReportName": "spot-io-cur", - "TimeUnit": "HOURLY", - "Format": "Parquet", - "Compression": "Parquet", - "S3Bucket": { - "Ref": "CostAndUsageBucket" - }, - "S3Prefix": "cost-and-usage-reports/", - "S3Region": "us-east-1", - "AdditionalSchemaElements": [ - "RESOURCES" - ], - "AdditionalArtifacts": [ - "ATHENA" - ], - "RefreshClosedReports": true, - "ReportVersioning": "OVERWRITE_REPORT" - } - }, - "S3BucketPolicy": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "CostAndUsageBucket" - }, - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "AllowCostAndUsageReportPut", - "Effect": "Allow", - "Principal": { - "Service": [ - "billingreports.amazonaws.com" - ] - }, - "Action": [ - "s3:PutObject" - ], - "Resource": { - "Fn::Sub": "arn:aws:s3:::${CostAndUsageBucket}/*" - } - }, - { - "Sid": "AllowCostAndUsageReport", - "Effect": "Allow", - "Principal": { - "Service": [ - "billingreports.amazonaws.com" - ] - }, - "Action": [ - "s3:GetBucketAcl", - "s3:GetBucketPolicy" - ], - "Resource": { - "Fn::Sub": "arn:aws:s3:::${CostAndUsageBucket}" - } - } - ] - } - } - }, - "SpotFinOpsManagedPolicy": { - "Type": "AWS::IAM::ManagedPolicy", - "Properties": { - "ManagedPolicyName": { - "Ref": "PolicyName" - }, - "Description": "Spot by NetApp Finops ReadOnly Policy", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "cloudformation:Describe*", - "cloudformation:EstimateTemplateCost", - "cloudformation:Get*", - "cloudformation:List*", - "cloudformation:ValidateTemplate", - "cloudformation:Detect*" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyCloudFormation" - }, - { - "Action": [ - "es:ListElasticsearchInstanceTypes", - "es:DescribeReservedElasticsearchInstanceOfferings", - "es:DescribeReservedElasticsearchInstances" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyElasticSearch" - }, - { - "Action": [ - "rds:DescribeReservedDBInstances", - "rds:DescribeDBInstances", - "rds:DescribeReservedDBInstancesOfferings", - "rds:ListTagsForResource" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyRDS" - }, - { - "Action": [ - "redshift:DescribeReservedNodeOfferings", - "redshift:DescribeReservedNodes", - "redshift:DescribeClusters" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyRedshift" - }, - { - "Action": [ - "elasticache:DescribeReservedCacheNodesOfferings", - "elasticache:DescribeReservedCacheNodes", - "elasticache:DescribeCacheClusters", - "elasticache:ListAllowedNodeTypeModifications", - "elasticache:ListTagsForResource" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyElasticache" - }, - { - "Action": [ - "dynamodb:DescribeReservedCapacityOfferings", - "dynamodb:DescribeReservedCapacity" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyDynamoDB" - }, - { - "Action": [ - "ec2:DescribeHostReservations", - "ec2:DescribeReservedInstances" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyEC2" - }, - { - "Action": [ - "savingsplans:DescribeSavingsPlanRates", - "savingsplans:DescribeSavingsPlans", - "savingsplans:DescribeSavingsPlansOfferingRates", - "savingsplans:DescribeSavingsPlansOfferings", - "savingsplans:ListTagsForResource" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlySavingsPlans" - }, - { - "Action": [ - "account:GetAccountInformation", - "billing:GetBillingData", - "billing:GetBillingDetails", - "billing:GetBillingNotifications", - "billing:GetBillingPreferences", - "billing:GetContractInformation", - "billing:GetCredits", - "billing:GetIAMAccessPreference", - "billing:GetSellerOfRecord", - "billing:ListBillingViews", - "ce:DescribeNotificationSubscription", - "ce:DescribeReport", - "ce:GetAnomalies", - "ce:GetAnomalyMonitors", - "ce:GetAnomalySubscriptions", - "ce:GetCostAndUsage", - "ce:GetCostAndUsageWithResources", - "ce:GetCostCategories", - "ce:GetCostForecast", - "ce:GetDimensionValues", - "ce:GetPreferences", - "ce:GetReservationCoverage", - "ce:GetReservationPurchaseRecommendation", - "ce:GetReservationUtilization", - "ce:GetRightsizingRecommendation", - "ce:GetSavingsPlansCoverage", - "ce:GetSavingsPlansPurchaseRecommendation", - "ce:GetSavingsPlansUtilization", - "ce:GetSavingsPlansUtilizationDetails", - "ce:GetTags", - "ce:GetUsageForecast", - "ce:ListCostAllocationTags", - "ce:ListSavingsPlansPurchaseRecommendationGeneration", - "consolidatedbilling:GetAccountBillingRole", - "consolidatedbilling:ListLinkedAccounts", - "cur:GetClassicReport", - "cur:GetClassicReportPreferences", - "cur:ValidateReportDestination", - "freetier:GetFreeTierAlertPreference", - "freetier:GetFreeTierUsage", - "invoicing:GetInvoiceEmailDeliveryPreferences", - "invoicing:GetInvoicePDF", - "invoicing:ListInvoiceSummaries", - "payments:GetPaymentInstrument", - "payments:GetPaymentStatus", - "payments:ListPaymentPreferences", - "tax:GetTaxInheritance", - "tax:GetTaxRegistrationDocument", - "tax:ListTaxRegistrations" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyViewBilling" - }, - { - "Action": [ - "cur:GetUsageReport", - "cur:DescribeReportDefinitions" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyViewUsage" - }, - { - "Action": [ - "s3:GetBucketLocation", - "s3:AbortMultipartUpload", - "s3:ListAccessPoints", - "s3:ListAccessPointsForObjectLambda", - "s3:ListAllMyBuckets", - "s3:ListBucket", - "s3:ListBucketMultipartUploads", - "s3:ListBucketVersions", - "s3:ListJobs", - "s3:ListMultiRegionAccessPoints", - "s3:ListMultipartUploadParts", - "s3:ListStorageLensConfigurations", - "s3:ListStorageLensConfigurations", - "s3:ListTagsForResource", - "s3:PutObject", - "s3:PutObjectTagging", - "s3:PutObjectAcl" - ], - "Resource": "arn:aws:s3:::sc-customer-*", - "Effect": "Allow", - "Sid": "S3SyncPermissions" - }, - { - "Action": [ - "s3:ListBucket", - "s3:ListBucketVersions", - "s3:ListBucketMultipartUploads", - "s3:GetBucketLocation" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - { - "Ref": "CostAndUsageBucket" - } - ] - ] - } - ], - "Effect": "Allow", - "Sid": "S3CURBucket" - }, - { - "Action": [ - "s3:Get*", - "s3:List*", - "s3:Describe*" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - { - "Ref": "CostAndUsageBucket" - }, - "/*" - ] - ] - } - ], - "Effect": "Allow", - "Sid": "S3CURObject" - } - ] - } - } - }, - "SpotFinOpsRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Principal": { - "AWS": [ - "arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole", - "arn:aws:iam::884866656237:role/Admin", - "arn:aws:iam::884866656237:role/DevRole", - "arn:aws:iam::884866656237:role/ReadOnly", - "arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole", - "arn:aws:iam::627743545735:role/Admin" - ] - }, - "Effect": "Allow" - } - ] - }, - "Description": "Spot by NetApp ReadOnly Finops IAM Role", - "ManagedPolicyArns": [ - { - "Ref": "SpotFinOpsManagedPolicy" - } - ], - "RoleName": { - "Ref": "RoleName" - } - } - } - } - } \ No newline at end of file diff --git a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-externalid.json b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-externalid.json deleted file mode 100644 index 35b8e3e..0000000 --- a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-externalid.json +++ /dev/null @@ -1,242 +0,0 @@ -{ - "AWSTemplateFormatVersion": "2010-09-09", - "Outputs": { - "SpotFinOpsRoleArn": { - "Value": { - "Fn::GetAtt": [ - "SpotFinOpsRole", - "Arn" - ] - } - }, - "CostAndUsageBucket": { - "Value": {"Ref": "CostAndUsageBucket"} - }, - "ExternalId": { - "Value": {"Ref": "ExternalId"} - } - }, - "Parameters": { - "CostAndUsageBucket": { - "Type": "String", - "Description": "The bucket name of where the *HOURLY* Cost and Usage Report is located. https://console.aws.amazon.com/billing/home?#/reports" - }, - "RoleName": { - "Type": "String", - "Default": "SpotByNetApp_Finops_ReadOnly" - }, - "PolicyName": { - "Type": "String", - "Default": "SpotByNetApp_Finops_ReadOnly_Policy" - }, - "ExternalId": { - "Type": "String" - } - }, - "Resources": { - "SpotFinOpsManagedPolicy": { - "Type": "AWS::IAM::ManagedPolicy", - "Properties": { - "ManagedPolicyName": { "Ref" : "PolicyName" }, - "Description": "Spot by NetApp Finops ReadOnly Policy", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "es:ListElasticsearchInstanceTypes", - "es:DescribeReservedElasticsearchInstanceOfferings", - "es:DescribeReservedElasticsearchInstances" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyElasticSearch" - }, - { - "Action": [ - "rds:DescribeReservedDBInstances", - "rds:DescribeDBInstances", - "rds:DescribeReservedDBInstancesOfferings" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyRDS" - }, - { - "Action": [ - "redshift:DescribeReservedNodeOfferings", - "redshift:DescribeReservedNodes", - "redshift:DescribeClusters" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyRedshift" - }, - { - "Action": [ - "elasticache:DescribeReservedCacheNodesOfferings", - "elasticache:DescribeReservedCacheNodes", - "elasticache:DescribeCacheClusters" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyElasticache" - }, - { - "Action": [ - "dynamodb:DescribeReservedCapacityOfferings", - "dynamodb:DescribeReservedCapacity" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyDynamoDB" - }, - { - "Action": [ - "ec2:DescribeHostReservations", - "ec2:DescribeReservedInstances" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyEC2" - }, - { - "Action": [ - "savingsplans:describe*", - "savingsplans:list*" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlySavingsPlans" - }, - { - "Action": [ - "aws-portal:ViewBilling", - "aws-portal:ViewUsage" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "BillingdeprecatedJuly6" - }, - { - "Action": [ - "cur:DescribeReportDefinitions", - "ce:Get*", - "ce:Describe*", - "ce:List*", - "account:GetAccountInformation", - "billing:Get*", - "consolidatedbilling:Get*", - "consolidatedbilling:List*", - "invoicing:List*", - "invoicing:Get*", - "cur:Get*", - "cur:Validate*", - "freetier:Get*" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "BillingPolicy" - }, - { - "Action": [ - "s3:GetBucketLocation", - "s3:ListBucketMultipartUploads", - "s3:AbortMultipartUpload", - "s3:ListMultipartUploadParts", - "s3:PutObject", - "s3:ListBucket", - "s3:List*", - "s3:PutObjectTagging", - "s3:PutObjectAcl" - ], - "Resource": "arn:aws:s3:::sc-customer-*", - "Effect": "Allow", - "Sid": "S3SyncPermissions" - }, - { - "Action": [ - "s3:ListBucket", - "s3:ListBucketVersions", - "s3:ListBucketMultipartUploads", - "s3:GetBucketLocation" - ], - "Resource": [ - { "Fn::Join" : [ "", [ "arn:aws:s3:::", { "Ref" : "CostAndUsageBucket" }]]} - ], - "Effect": "Allow", - "Sid": "S3CURBucket" - }, - { - "Action": [ - "s3:get*", - "s3:List*", - "s3:Describe*" - ], - "Resource": [ - { "Fn::Join" : [ "", [ "arn:aws:s3:::", { "Ref" : "CostAndUsageBucket" },"/*"]]} - ], - "Effect": "Allow", - "Sid": "S3CURObject" - } - ] - } - } - }, - "SpotFinOpsRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Principal": { - "AWS": ["arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole", - "arn:aws:iam::884866656237:role/Admin", - "arn:aws:iam::884866656237:role/DevRole", - "arn:aws:iam::884866656237:role/ReadOnly", - "arn:aws:iam::627743545735:role/Admin", - "arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole"] - }, - "Effect": "Allow", - "Condition": { - "StringEquals": { - "sts:ExternalId": { - "Ref": "ExternalId" - } - } - } - } - ] - }, - "Description" : "Spot by NetApp ReadOnly Finops IAM Role", - "ManagedPolicyArns": [ - { - "Ref": "SpotFinOpsManagedPolicy" - }, - "arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess" - ], - "RoleName" : { "Ref" : "RoleName" } - } - } - } -} \ No newline at end of file diff --git a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume.json b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume.json deleted file mode 100644 index c840201..0000000 --- a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only-limited-assume.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "AWSTemplateFormatVersion": "2010-09-09", - "Outputs": { - "SpotFinOpsRoleArn": { - "Value": { - "Fn::GetAtt": [ - "SpotFinOpsRole", - "Arn" - ] - } - } - }, - "Parameters": { - "CostAndUsageBucket": { - "Type": "String", - "Description": "The bucket name of where the *HOURLY* Cost and Usage Report is located. https://console.aws.amazon.com/billing/home?#/reports" - }, - "RoleName": { - "Type": "String", - "Default": "SpotByNetApp_Finops_ReadOnly" - }, - "PolicyName": { - "Type": "String", - "Default": "SpotByNetApp_Finops_ReadOnly_Policy" - } - }, - "Resources": { - "SpotFinOpsManagedPolicy": { - "Type": "AWS::IAM::ManagedPolicy", - "Properties": { - "ManagedPolicyName": { "Ref" : "PolicyName" }, - "Description": "Spot by NetApp Finops ReadOnly Policy", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "es:ListElasticsearchInstanceTypes", - "es:DescribeReservedElasticsearchInstanceOfferings", - "es:DescribeReservedElasticsearchInstances" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyElasticSearch" - }, - { - "Action": [ - "rds:DescribeReservedDBInstances", - "rds:DescribeDBInstances", - "rds:DescribeReservedDBInstancesOfferings" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyRDS" - }, - { - "Action": [ - "redshift:DescribeReservedNodeOfferings", - "redshift:DescribeReservedNodes", - "redshift:DescribeClusters" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyRedshift" - }, - { - "Action": [ - "elasticache:DescribeReservedCacheNodesOfferings", - "elasticache:DescribeReservedCacheNodes", - "elasticache:DescribeCacheClusters" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyElasticache" - }, - { - "Action": [ - "dynamodb:DescribeReservedCapacityOfferings", - "dynamodb:DescribeReservedCapacity" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyDynamoDB" - }, - { - "Action": [ - "ec2:DescribeHostReservations", - "ec2:DescribeReservedInstances" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlyEC2" - }, - { - "Action": [ - "savingsplans:describe*", - "savingsplans:list*" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "ReadOnlySavingsPlans" - }, - { - "Action": [ - "aws-portal:ViewBilling", - "aws-portal:ViewUsage" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "BillingdeprecatedJuly6" - }, - { - "Action": [ - "cur:DescribeReportDefinitions", - "ce:Get*", - "ce:Describe*", - "ce:List*", - "account:GetAccountInformation", - "billing:Get*", - "consolidatedbilling:Get*", - "consolidatedbilling:List*", - "invoicing:List*", - "invoicing:Get*", - "cur:Get*", - "cur:Validate*", - "freetier:Get*" - ], - "Resource": [ - "*" - ], - "Effect": "Allow", - "Sid": "BillingPolicy" - }, - { - "Action": [ - "s3:GetBucketLocation", - "s3:ListBucketMultipartUploads", - "s3:AbortMultipartUpload", - "s3:ListMultipartUploadParts", - "s3:PutObject", - "s3:ListBucket", - "s3:List*", - "s3:PutObjectTagging", - "s3:PutObjectAcl" - ], - "Resource": "arn:aws:s3:::sc-customer-*", - "Effect": "Allow", - "Sid": "S3SyncPermissions" - }, - { - "Action": [ - "s3:ListBucket", - "s3:ListBucketVersions", - "s3:ListBucketMultipartUploads", - "s3:GetBucketLocation" - ], - "Resource": [ - { "Fn::Join" : [ "", [ "arn:aws:s3:::", { "Ref" : "CostAndUsageBucket" }]]} - ], - "Effect": "Allow", - "Sid": "S3CURBucket" - }, - { - "Action": [ - "s3:get*", - "s3:List*", - "s3:Describe*" - ], - "Resource": [ - { "Fn::Join" : [ "", [ "arn:aws:s3:::", { "Ref" : "CostAndUsageBucket" },"/*"]]} - ], - "Effect": "Allow", - "Sid": "S3CURObject" - } - ] - } - } - }, - "SpotFinOpsRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Principal": { - "AWS": ["arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole", - "arn:aws:iam::884866656237:role/Admin", - "arn:aws:iam::884866656237:role/DevRole", - "arn:aws:iam::884866656237:role/ReadOnly", - "arn:aws:iam::627743545735:role/Admin", - "arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole"] - }, - "Effect": "Allow" - } - ] - }, - "Description" : "Spot by NetApp ReadOnly Finops IAM Role", - "ManagedPolicyArns": [ - { - "Ref": "SpotFinOpsManagedPolicy" - }, - "arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess" - ], - "RoleName" : { "Ref" : "RoleName" } - } - } - } -} \ No newline at end of file diff --git a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only.json b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only.json index 81af189..5f6a16f 100644 --- a/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only.json +++ b/CloudFormation/IAM-Roles/Eco/json/spot-iam-finopsrole-stack-restricted-read-only.json @@ -22,6 +22,9 @@ "PolicyName": { "Type": "String", "Default": "SpotByNetApp_Finops_ReadOnly_Policy" + }, + "ExternalId": { + "Type": "String" } }, "Resources": { @@ -294,7 +297,14 @@ "arn:aws:iam::627743545735:root" ] }, - "Effect": "Allow" + "Effect": "Allow", + "Condition": { + "StringEquals": { + "sts:ExternalId": { + "Ref": "ExternalId" + } + } + } } ] }, diff --git a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.yaml b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.yaml deleted file mode 100644 index 0459fa4..0000000 --- a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-cur-and-bucket.yaml +++ /dev/null @@ -1,288 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Outputs: - SpotFinOpsRoleArn: - Value: - 'Fn::GetAtt': - - SpotFinOpsRole - - Arn -Parameters: - CostAndUsageBucket: - Type: String - Description: >- - The bucket name of where the *HOURLY* Cost and Usage Report is located. - https://console.aws.amazon.com/billing/home?#/reports - RoleName: - Type: String - Default: SpotByNetApp_Finops_ReadOnly - PolicyName: - Type: String - Default: SpotByNetApp_Finops_ReadOnly_Policy -Resources: - SpotCurBucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: - 'Fn::Sub': - - '${CostAndUsageBucket}${RandomGUID}' - - RandomGUID: - 'Fn::Select': - - 0 - - 'Fn::Split': - - '-' - - 'Fn::Select': - - 2 - - 'Fn::Split': - - / - - Ref: 'AWS::StackId' - CostAndUsageReport: - Type: 'AWS::CUR::ReportDefinition' - Properties: - ReportName: spot-io-cur - TimeUnit: HOURLY - Format: Parquet - Compression: Parquet - S3Bucket: - Ref: CostAndUsageBucket - S3Prefix: cost-and-usage-reports/ - S3Region: us-east-1 - AdditionalSchemaElements: - - RESOURCES - AdditionalArtifacts: - - ATHENA - RefreshClosedReports: true - ReportVersioning: OVERWRITE_REPORT - S3BucketPolicy: - Type: 'AWS::S3::BucketPolicy' - Properties: - Bucket: - Ref: CostAndUsageBucket - PolicyDocument: - Version: '2012-10-17' - Statement: - - Sid: AllowCostAndUsageReportPut - Effect: Allow - Principal: - Service: - - billingreports.amazonaws.com - Action: - - 's3:PutObject' - Resource: - 'Fn::Sub': 'arn:aws:s3:::${CostAndUsageBucket}/*' - - Sid: AllowCostAndUsageReport - Effect: Allow - Principal: - Service: - - billingreports.amazonaws.com - Action: - - 's3:GetBucketAcl' - - 's3:GetBucketPolicy' - Resource: - 'Fn::Sub': 'arn:aws:s3:::${CostAndUsageBucket}' - SpotFinOpsManagedPolicy: - Type: 'AWS::IAM::ManagedPolicy' - Properties: - ManagedPolicyName: - Ref: PolicyName - Description: Spot by NetApp Finops ReadOnly Policy - PolicyDocument: - Version: '2012-10-17' - Statement: - - Action: - - 'cloudformation:Describe*' - - 'cloudformation:EstimateTemplateCost' - - 'cloudformation:Get*' - - 'cloudformation:List*' - - 'cloudformation:ValidateTemplate' - - 'cloudformation:Detect*' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyCloudFormation - - Action: - - 'es:ListElasticsearchInstanceTypes' - - 'es:DescribeReservedElasticsearchInstanceOfferings' - - 'es:DescribeReservedElasticsearchInstances' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyElasticSearch - - Action: - - 'rds:DescribeReservedDBInstances' - - 'rds:DescribeDBInstances' - - 'rds:DescribeReservedDBInstancesOfferings' - - 'rds:ListTagsForResource' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyRDS - - Action: - - 'redshift:DescribeReservedNodeOfferings' - - 'redshift:DescribeReservedNodes' - - 'redshift:DescribeClusters' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyRedshift - - Action: - - 'elasticache:DescribeReservedCacheNodesOfferings' - - 'elasticache:DescribeReservedCacheNodes' - - 'elasticache:DescribeCacheClusters' - - 'elasticache:ListAllowedNodeTypeModifications' - - 'elasticache:ListTagsForResource' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyElasticache - - Action: - - 'dynamodb:DescribeReservedCapacityOfferings' - - 'dynamodb:DescribeReservedCapacity' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyDynamoDB - - Action: - - 'ec2:DescribeHostReservations' - - 'ec2:DescribeReservedInstances' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyEC2 - - Action: - - 'savingsplans:DescribeSavingsPlanRates' - - 'savingsplans:DescribeSavingsPlans' - - 'savingsplans:DescribeSavingsPlansOfferingRates' - - 'savingsplans:DescribeSavingsPlansOfferings' - - 'savingsplans:ListTagsForResource' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlySavingsPlans - - Action: - - 'account:GetAccountInformation' - - 'billing:GetBillingData' - - 'billing:GetBillingDetails' - - 'billing:GetBillingNotifications' - - 'billing:GetBillingPreferences' - - 'billing:GetContractInformation' - - 'billing:GetCredits' - - 'billing:GetIAMAccessPreference' - - 'billing:GetSellerOfRecord' - - 'billing:ListBillingViews' - - 'ce:DescribeNotificationSubscription' - - 'ce:DescribeReport' - - 'ce:GetAnomalies' - - 'ce:GetAnomalyMonitors' - - 'ce:GetAnomalySubscriptions' - - 'ce:GetCostAndUsage' - - 'ce:GetCostAndUsageWithResources' - - 'ce:GetCostCategories' - - 'ce:GetCostForecast' - - 'ce:GetDimensionValues' - - 'ce:GetPreferences' - - 'ce:GetReservationCoverage' - - 'ce:GetReservationPurchaseRecommendation' - - 'ce:GetReservationUtilization' - - 'ce:GetRightsizingRecommendation' - - 'ce:GetSavingsPlansCoverage' - - 'ce:GetSavingsPlansPurchaseRecommendation' - - 'ce:GetSavingsPlansUtilization' - - 'ce:GetSavingsPlansUtilizationDetails' - - 'ce:GetTags' - - 'ce:GetUsageForecast' - - 'ce:ListCostAllocationTags' - - 'ce:ListSavingsPlansPurchaseRecommendationGeneration' - - 'consolidatedbilling:GetAccountBillingRole' - - 'consolidatedbilling:ListLinkedAccounts' - - 'cur:GetClassicReport' - - 'cur:GetClassicReportPreferences' - - 'cur:ValidateReportDestination' - - 'freetier:GetFreeTierAlertPreference' - - 'freetier:GetFreeTierUsage' - - 'invoicing:GetInvoiceEmailDeliveryPreferences' - - 'invoicing:GetInvoicePDF' - - 'invoicing:ListInvoiceSummaries' - - 'payments:GetPaymentInstrument' - - 'payments:GetPaymentStatus' - - 'payments:ListPaymentPreferences' - - 'tax:GetTaxInheritance' - - 'tax:GetTaxRegistrationDocument' - - 'tax:ListTaxRegistrations' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyViewBilling - - Action: - - 'cur:GetUsageReport' - - 'cur:DescribeReportDefinitions' - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyViewUsage - - Action: - - 's3:GetBucketLocation' - - 's3:AbortMultipartUpload' - - 's3:ListAccessPoints' - - 's3:ListAccessPointsForObjectLambda' - - 's3:ListAllMyBuckets' - - 's3:ListBucket' - - 's3:ListBucketMultipartUploads' - - 's3:ListBucketVersions' - - 's3:ListJobs' - - 's3:ListMultiRegionAccessPoints' - - 's3:ListMultipartUploadParts' - - 's3:ListStorageLensConfigurations' - - 's3:ListStorageLensConfigurations' - - 's3:ListTagsForResource' - - 's3:PutObject' - - 's3:PutObjectTagging' - - 's3:PutObjectAcl' - Resource: 'arn:aws:s3:::sc-customer-*' - Effect: Allow - Sid: S3SyncPermissions - - Action: - - 's3:ListBucket' - - 's3:ListBucketVersions' - - 's3:ListBucketMultipartUploads' - - 's3:GetBucketLocation' - Resource: - - 'Fn::Join': - - '' - - - 'arn:aws:s3:::' - - Ref: CostAndUsageBucket - Effect: Allow - Sid: S3CURBucket - - Action: - - 's3:Get*' - - 's3:List*' - - 's3:Describe*' - Resource: - - 'Fn::Join': - - '' - - - 'arn:aws:s3:::' - - Ref: CostAndUsageBucket - - /* - Effect: Allow - Sid: S3CURObject - SpotFinOpsRole: - Type: 'AWS::IAM::Role' - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Action: 'sts:AssumeRole' - Principal: - AWS: - - >- - arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole - - 'arn:aws:iam::884866656237:role/Admin' - - 'arn:aws:iam::884866656237:role/DevRole' - - 'arn:aws:iam::884866656237:role/ReadOnly' - - >- - arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole - - 'arn:aws:iam::627743545735:role/Admin' - Effect: Allow - Description: Spot by NetApp ReadOnly Finops IAM Role - ManagedPolicyArns: - - Ref: SpotFinOpsManagedPolicy - RoleName: - Ref: RoleName diff --git a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-externalid.yaml b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-externalid.yaml deleted file mode 100644 index 9065038..0000000 --- a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume-with-externalid.yaml +++ /dev/null @@ -1,168 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Outputs: - SpotFinOpsRoleArn: - Value: !GetAtt 'SpotFinOpsRole.Arn' - CostAndUsageBucket: - Value: !Ref 'CostAndUsageBucket' - ExternalId: - Value: !Ref 'ExternalId' -Parameters: - CostAndUsageBucket: - Type: String - Description: The bucket name of where the *HOURLY* Cost and Usage Report is located. https://console.aws.amazon.com/billing/home?#/reports - RoleName: - Type: String - Default: SpotByNetApp_Finops_ReadOnly - PolicyName: - Type: String - Default: SpotByNetApp_Finops_ReadOnly_Policy - ExternalId: - Type: String -Resources: - SpotFinOpsManagedPolicy: - Type: AWS::IAM::ManagedPolicy - Properties: - ManagedPolicyName: !Ref 'PolicyName' - Description: Spot by NetApp Finops ReadOnly Policy - PolicyDocument: - Version: '2012-10-17' - Statement: - - Action: - - es:ListElasticsearchInstanceTypes - - es:DescribeReservedElasticsearchInstanceOfferings - - es:DescribeReservedElasticsearchInstances - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyElasticSearch - - Action: - - rds:DescribeReservedDBInstances - - rds:DescribeDBInstances - - rds:DescribeReservedDBInstancesOfferings - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyRDS - - Action: - - redshift:DescribeReservedNodeOfferings - - redshift:DescribeReservedNodes - - redshift:DescribeClusters - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyRedshift - - Action: - - elasticache:DescribeReservedCacheNodesOfferings - - elasticache:DescribeReservedCacheNodes - - elasticache:DescribeCacheClusters - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyElasticache - - Action: - - dynamodb:DescribeReservedCapacityOfferings - - dynamodb:DescribeReservedCapacity - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyDynamoDB - - Action: - - ec2:DescribeHostReservations - - ec2:DescribeReservedInstances - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyEC2 - - Action: - - savingsplans:describe* - - savingsplans:list* - Resource: - - '*' - Effect: Allow - Sid: ReadOnlySavingsPlans - - Action: - - aws-portal:ViewBilling - - aws-portal:ViewUsage - Resource: - - '*' - Effect: Allow - Sid: BillingdeprecatedJuly6 - - Action: - - cur:DescribeReportDefinitions - - ce:Get* - - ce:Describe* - - ce:List* - - account:GetAccountInformation - - billing:Get* - - consolidatedbilling:Get* - - consolidatedbilling:List* - - invoicing:List* - - invoicing:Get* - - cur:Get* - - cur:Validate* - - freetier:Get* - Resource: - - '*' - Effect: Allow - Sid: BillingPolicy - - Action: - - s3:GetBucketLocation - - s3:ListBucketMultipartUploads - - s3:AbortMultipartUpload - - s3:ListMultipartUploadParts - - s3:PutObject - - s3:ListBucket - - s3:List* - - s3:PutObjectTagging - - s3:PutObjectAcl - Resource: arn:aws:s3:::sc-customer-* - Effect: Allow - Sid: S3SyncPermissions - - Action: - - s3:ListBucket - - s3:ListBucketVersions - - s3:ListBucketMultipartUploads - - s3:GetBucketLocation - Resource: - - !Join - - '' - - - 'arn:aws:s3:::' - - !Ref 'CostAndUsageBucket' - Effect: Allow - Sid: S3CURBucket - - Action: - - s3:get* - - s3:List* - - s3:Describe* - Resource: - - !Join - - '' - - - 'arn:aws:s3:::' - - !Ref 'CostAndUsageBucket' - - /* - Effect: Allow - Sid: S3CURObject - SpotFinOpsRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Action: sts:AssumeRole - Principal: - AWS: - - arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole - - arn:aws:iam::884866656237:role/Admin - - arn:aws:iam::884866656237:role/DevRole - - arn:aws:iam::884866656237:role/ReadOnly - - arn:aws:iam::627743545735:role/Admin - - arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole - Effect: Allow - Condition: - StringEquals: - sts:ExternalId: !Ref 'ExternalId' - Description: Spot by NetApp ReadOnly Finops IAM Role - ManagedPolicyArns: - - !Ref 'SpotFinOpsManagedPolicy' - - arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess - RoleName: !Ref 'RoleName' diff --git a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume.yaml b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume.yaml deleted file mode 100644 index 2411249..0000000 --- a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only-limited-assume.yaml +++ /dev/null @@ -1,159 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Outputs: - SpotFinOpsRoleArn: - Value: !GetAtt 'SpotFinOpsRole.Arn' -Parameters: - CostAndUsageBucket: - Type: String - Description: The bucket name of where the *HOURLY* Cost and Usage Report is located. https://console.aws.amazon.com/billing/home?#/reports - RoleName: - Type: String - Default: SpotByNetApp_Finops_ReadOnly - PolicyName: - Type: String - Default: SpotByNetApp_Finops_ReadOnly_Policy -Resources: - SpotFinOpsManagedPolicy: - Type: AWS::IAM::ManagedPolicy - Properties: - ManagedPolicyName: !Ref 'PolicyName' - Description: Spot by NetApp Finops ReadOnly Policy - PolicyDocument: - Version: '2012-10-17' - Statement: - - Action: - - es:ListElasticsearchInstanceTypes - - es:DescribeReservedElasticsearchInstanceOfferings - - es:DescribeReservedElasticsearchInstances - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyElasticSearch - - Action: - - rds:DescribeReservedDBInstances - - rds:DescribeDBInstances - - rds:DescribeReservedDBInstancesOfferings - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyRDS - - Action: - - redshift:DescribeReservedNodeOfferings - - redshift:DescribeReservedNodes - - redshift:DescribeClusters - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyRedshift - - Action: - - elasticache:DescribeReservedCacheNodesOfferings - - elasticache:DescribeReservedCacheNodes - - elasticache:DescribeCacheClusters - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyElasticache - - Action: - - dynamodb:DescribeReservedCapacityOfferings - - dynamodb:DescribeReservedCapacity - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyDynamoDB - - Action: - - ec2:DescribeHostReservations - - ec2:DescribeReservedInstances - Resource: - - '*' - Effect: Allow - Sid: ReadOnlyEC2 - - Action: - - savingsplans:describe* - - savingsplans:list* - Resource: - - '*' - Effect: Allow - Sid: ReadOnlySavingsPlans - - Action: - - aws-portal:ViewBilling - - aws-portal:ViewUsage - Resource: - - '*' - Effect: Allow - Sid: BillingdeprecatedJuly6 - - Action: - - cur:DescribeReportDefinitions - - ce:Get* - - ce:Describe* - - ce:List* - - account:GetAccountInformation - - billing:Get* - - consolidatedbilling:Get* - - consolidatedbilling:List* - - invoicing:List* - - invoicing:Get* - - cur:Get* - - cur:Validate* - - freetier:Get* - Resource: - - '*' - Effect: Allow - Sid: BillingPolicy - - Action: - - s3:GetBucketLocation - - s3:ListBucketMultipartUploads - - s3:AbortMultipartUpload - - s3:ListMultipartUploadParts - - s3:PutObject - - s3:ListBucket - - s3:List* - - s3:PutObjectTagging - - s3:PutObjectAcl - Resource: arn:aws:s3:::sc-customer-* - Effect: Allow - Sid: S3SyncPermissions - - Action: - - s3:ListBucket - - s3:ListBucketVersions - - s3:ListBucketMultipartUploads - - s3:GetBucketLocation - Resource: - - !Join - - '' - - - 'arn:aws:s3:::' - - !Ref 'CostAndUsageBucket' - Effect: Allow - Sid: S3CURBucket - - Action: - - s3:get* - - s3:List* - - s3:Describe* - Resource: - - !Join - - '' - - - 'arn:aws:s3:::' - - !Ref 'CostAndUsageBucket' - - /* - Effect: Allow - Sid: S3CURObject - SpotFinOpsRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Action: sts:AssumeRole - Principal: - AWS: - - arn:aws:iam::884866656237:role/eco-base-cdk-stack-globalclientassumerole - - arn:aws:iam::884866656237:role/Admin - - arn:aws:iam::884866656237:role/DevRole - - arn:aws:iam::884866656237:role/ReadOnly - - arn:aws:iam::627743545735:role/Admin - - arn:aws:iam::627743545735:role/eco-base-cdk-stack-globalclientassumerole - Effect: Allow - Description: Spot by NetApp ReadOnly Finops IAM Role - ManagedPolicyArns: - - !Ref 'SpotFinOpsManagedPolicy' - - arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess - RoleName: !Ref 'RoleName' diff --git a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only.yaml b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only.yaml index f2d1d4e..e6ea854 100644 --- a/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only.yaml +++ b/CloudFormation/IAM-Roles/Eco/yaml/spot-iam-finopsrole-stack-restricted-read-only.yaml @@ -17,6 +17,8 @@ Parameters: PolicyName: Type: String Default: SpotByNetApp_Finops_ReadOnly_Policy + ExternalId: + Type: String Resources: SpotFinOpsManagedPolicy: Type: 'AWS::IAM::ManagedPolicy' @@ -215,6 +217,9 @@ Resources: - 'arn:aws:iam::884866656237:root' - 'arn:aws:iam::627743545735:root' Effect: Allow + Condition: + StringEquals: + sts:ExternalId: !Ref 'ExternalId' Description: Spot by NetApp ReadOnly Finops IAM Role ManagedPolicyArns: - Ref: SpotFinOpsManagedPolicy From 8f24bb10930ec4a217340c36a3a83089a1e58edf Mon Sep 17 00:00:00 2001 From: Scott Sullivan <126610240+sc0tt-sullivan@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:51:32 -0400 Subject: [PATCH 07/14] SF-8332 - Add support for onboarding all subscriptions in a tenant (#98) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SF-8332 - Add support for onboarding all subscriptions in a tenant. A… (#1) * SF-8332 - Add support for onboarding all subscriptions in a tenant. Add support for onboarding the Cost Intelligence product. * PR comments. * SF-9925 - Added error handling for individual subscriptions. Updated (#2) * SF-9925 - Added error handling for individual subscriptions. Updated logging. * Added comments. Added failed subscriptions to output. * logging tweaks * renamed extension install method --- .../azure-automatic-role-assignment.py | 380 +++++++++++++----- 1 file changed, 288 insertions(+), 92 deletions(-) diff --git a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py index e06867b..7ee6a33 100644 --- a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py +++ b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py @@ -1,15 +1,17 @@ #!/usr/bin/env import argparse -import json import re import subprocess import time +from datetime import datetime import requests from azure.identity import DefaultAzureCredential from azure.mgmt.subscription import SubscriptionClient +from requests import ConnectTimeout from spotinst_sdk2 import SpotinstSession +from spotinst_sdk2.client import Client, SpotinstClientException from spotinst_sdk2.models.setup.azure import * CUSTOM_ROLE_NAME = "{customRoleName}" @@ -18,6 +20,36 @@ "https://nirtest2.s3-us-west-2.amazonaws.com/custom_role_file.json" ) HTTP_OK_RESPONSE_STATUSES = range(200, 300) +SPOT_API_BASE_URL = 'https://api.spotinst.io' +SPOT_SETUP_CI_PATH = "/cbi/v1/setup/account" + + +class Products: + CORE = 'core' + COST_INTELLIGENCE = 'cost-intelligence' + + +class BuiltInAzureRoles: + READER = 'READER' + + +class LogLevel: + ERROR = "ERROR" + INFO = "INFO" + + +def log(message, log_level=LogLevel.INFO): + print(f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]} [{log_level}] {message}") + + +def get_all_subscriptions_in_tenant(): + """ + Retrieves all the subscriptions in the tenant (that the use can access). + Returns: + list(str): The subscription ids in the tenant. + """ + subscription_ids = run_command("az account subscription list --query [].subscriptionId") + return subscription_ids def get_subscription_name(subscription_id): @@ -33,24 +65,52 @@ def get_subscription_name(subscription_id): credential = DefaultAzureCredential() subscription_client = SubscriptionClient(credential) subscription = subscription_client.subscriptions.get(subscription_id) + return subscription.display_name -def create_spot_account(name, token): +def get_or_create_spot_account(subscription_id, name, token): """ Creates a spot account with the given name and token. Args: + subscription_id (str) : External provider id. name (str): The name of the spot account. token (str): The authentication token. Returns: str: The ID of the created account. """ - session = SpotinstSession(auth_token=token) - client = session.client("admin") - account_result = client.create_account(name) - return account_result["id"] + try: + session = SpotinstSession(auth_token=token, base_url=SPOT_API_BASE_URL) + except SpotinstClientException as e: + log(f"Spotinst token is invalid.", LogLevel.ERROR) + raise e + + client = session.client("admin", timeout=15) + + try: + existing_accounts = [account["account_id"] for account in client.get_accounts() if + account["provider_external_id"] == subscription_id and account["cloud_provider"] == 'AZURE'] + except ConnectTimeout as e: + log(f"Could not reach Spot API. Please try again later.", LogLevel.ERROR) + raise e + + except Exception as e: + log(f"An exception of type {type(e).__name__} occurred while attempting to retrieve Spot Account information. {str(e)}", LogLevel.ERROR) + raise e + + account_id = existing_accounts.pop() if len(existing_accounts) > 0 else None + + if not account_id: + # there is no existing spot account so create it + account_result = client.create_account(name) + account_id = account_result["id"] + log(f"No Spot Account was not found for the subscription. Created Spot Account {account_id} for subscription {subscription_id}") + else: + log(f"Existing Spot Account found {account_id} for subscription {subscription_id}") + + return account_id def run_command(cmd, *args): @@ -68,17 +128,17 @@ def run_command(cmd, *args): Exception: If the command fails to run, an exception is raised with the command and error message. Example: - >>> run_command("ls", "-l") + #>>> run_command("ls", "-l") {'file1.txt': 'rw-r--r--', 'file2.txt': 'rw-rw-r--'} """ - print(f"Run command: {cmd}") + log(f"Running command: {cmd}") cmd_args = cmd.split() cmd_to_run = cmd_args + list(args) run_process_result = subprocess.run( cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8" - ) # Maybe need to change to check output because in python5 there isn't encoding + ) # Maybe need to change to check output because in python there isn't encoding # try: # output = subprocess.check_output( @@ -97,9 +157,10 @@ def run_command(cmd, *args): ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") cleaned_command_result = ansi_escape.sub("", run_process_result.stdout) - result = json.loads(cleaned_command_result) + print(cleaned_command_result) + result = None if (cleaned_command_result is None) or (cleaned_command_result == '') else json.loads(cleaned_command_result) - print("Succeeded to run command: {cmd}") + log(f"Succeeded running command: {cmd}") return result @@ -115,11 +176,11 @@ def display_result(subscription, create_or_get_service_principal_response): Returns: None """ - print("Your credentials details:") - print(f"Application (client) ID: {create_or_get_service_principal_response['appId']}") - print(f"Client Secret: {create_or_get_service_principal_response['password']}") - print(f"Tenant ID: {create_or_get_service_principal_response['tenant']}") - print(f"Subscription ID: {subscription}") + log("Your credentials details:") + log(f"Application (client) ID: {create_or_get_service_principal_response['appId']}") + log(f"Client Secret: {create_or_get_service_principal_response['password']}") + log(f"Tenant ID: {create_or_get_service_principal_response['tenant']}") + log(f"Subscription ID: {subscription}") def set_azure_credentials( @@ -139,16 +200,25 @@ def set_azure_credentials( Returns: bool: True if credentials were set successfully, False otherwise """ - session = SpotinstSession(auth_token=token) - client = session.client("setup_azure") - client.account_id = account_id - azure_credentials = AzureCredentials( - client_id=client_id, - client_secret=client_secret, - tenant_id=tenant_id, - subscription_id=subscription_id, - ) - return client.set_credentials(azure_credentials) + log(f"Updating linked credentials for Spot account {account_id}") + try: + session = SpotinstSession(auth_token=token, base_url=SPOT_API_BASE_URL) + client = session.client("setup_azure", timeout=15) + client.account_id = account_id + azure_credentials = AzureCredentials( + client_id=client_id, + client_secret=client_secret, + tenant_id=tenant_id, + subscription_id=subscription_id, + ) + result = client.set_credentials(azure_credentials) + except SpotinstClientException as e: + # the new credentials may have failed to be validated because of Azure propagation latency. try again after a brief delay + time.sleep(15) + result = client.set_credentials(azure_credentials) + + log(f"Successfully updated linked credentials.") + return result # todo nir - from where take the file in case of using --path param @@ -194,21 +264,28 @@ def create_required_parameters_for_spot_registrations( custom_role_name, custom_role_json_local_path, service_principal_name, - app_registration_id=None, + products, + app_registration_id=None ): if does_subscription_exist_for_account(subscription): - custom_role_name = create_custom_role( - custom_role_name, custom_role_json_local_path, subscription - ) - print("Waiting 90 seconds for role to propagate.") - time.sleep(90) + roles = [] + if Products.CORE in products: + custom_role_name = get_or_create_custom_role( + custom_role_name, custom_role_json_local_path, subscription + ) + roles.append(custom_role_name) + + if Products.COST_INTELLIGENCE in products: + # cost intelligence requires the Azure built-in READER role + roles.append(BuiltInAzureRoles.READER) + if app_registration_id is not None: create_or_get_service_principal_response = get_service_principal( - app_registration_id, custom_role_name, subscription, service_principal_name + app_registration_id, roles, subscription, service_principal_name ) else: create_or_get_service_principal_response = create_service_principal( - custom_role_name, service_principal_name, subscription + roles, service_principal_name, subscription ) result = create_or_get_service_principal_response @@ -218,14 +295,15 @@ def create_required_parameters_for_spot_registrations( return result -def get_service_principal(app_registration_id, custom_role_name, subscription, spot_account_id): +def get_service_principal(app_registration_id, roles, subscription, credential_name): """ Generate a service principal for the given app registration ID, assign a custom role to the service principal, create a secret for the app registration, and return the result of creating the secret. Parameters: - app_registration_id (str): The ID of the app registration. - - custom_role_name (str): The name of the custom role to assign to the service principal. + - roles (list(str)): The names of the roles to assign to the service principal. - subscription (str): The ID of the subscription. + - credential_name (str): The display name to use for the credential. Returns: - create_secret_result (dict): The result of creating the secret for the app registration. @@ -233,16 +311,18 @@ def get_service_principal(app_registration_id, custom_role_name, subscription, s app_reg_show_cmd = f"az ad sp show --id {app_registration_id}" run_command(app_reg_show_cmd) - # Assign the custom role to the service principal - app_reg_role_assignment_cmd = f"az role assignment create --assignee {app_registration_id} --role {custom_role_name} --scope /subscriptions/{subscription}" - run_command(app_reg_role_assignment_cmd) + # Assign the roles to the service principal + for role in roles: + app_reg_role_assignment_cmd = f"az role assignment create --assignee {app_registration_id} --role {role} --scope /subscriptions/{subscription}" + run_command(app_reg_role_assignment_cmd) # Create a secret for the App Registration - create_secret_cmd = f"az ad app credential reset --id {app_registration_id} --append --display-name {spot_account_id}" + create_secret_cmd = f"az ad app credential reset --id {app_registration_id} --append --display-name {credential_name}" create_secret_result = run_command(create_secret_cmd) return create_secret_result + def does_subscription_exist_for_account(subscription): """ Check if a subscription exists for an account. @@ -253,41 +333,64 @@ def does_subscription_exist_for_account(subscription): Returns: bool: True if the subscription exists for the account, False otherwise. """ - get_account_cmd = "az account list --output json --all" + get_account_cmd = "az account list --all" accounts = list(run_command(get_account_cmd)) account_property_with_subscription = next( (account for account in accounts if account["id"] == subscription), None ) if account_property_with_subscription is not None: - print(f"Found the subscription: {subscription} for account: {account_property_with_subscription}") + log(f"Found the subscription: {subscription} for account: {account_property_with_subscription}") return True else: return False -def create_service_principal(custom_role_name, service_principal_name, subscription): +def create_service_principal(roles, service_principal_name, subscription): """ Creates a service principal with the given custom role name, service principal name, and subscription. Args: - custom_role_name (str): The name of the custom role. + roles (list(str)): The names of the roles to assign to the service principal. service_principal_name (str): The name of the service principal. subscription (str): The subscription ID. Returns: str: The result of creating the service principal. """ - print("Create service principal") - create_service_principal_cmd = f"az ad sp create-for-rbac --name {service_principal_name} --role {custom_role_name} --scopes /subscriptions/{subscription}" - result = run_command(create_service_principal_cmd) - - print(f"Finished to create service principal: {result}") + log("Creating service principal") + create_service_principal_cmd = f"az ad sp create-for-rbac" + + result = run_command(create_service_principal_cmd, + "--name", service_principal_name) + app_registration_id = result.get('appId') + + # Get the object_id for the service principal. This is used with --assignee-object-id to avoid errors caused by propagation latency in AAD Graph. + # see https://learn.microsoft.com/en-us/cli/azure/role/assignment?view=azure-cli-latest#az-role-assignment-create + get_app_object_id_cmd = f"az ad sp show --id {app_registration_id}" + object_id_result = run_command(get_app_object_id_cmd) + app_object_id = object_id_result.get('id') + + # Assign the roles to the service principal + for role in roles: + app_reg_role_assignment_cmd = f"az role assignment create --assignee-object-id {app_object_id} --assignee-principal-type ServicePrincipal" + try: + run_command(app_reg_role_assignment_cmd, + "--role", role, + "--scope", f"/subscriptions/{subscription}") + except: + # the role assignment may have failed because of propagation latency. try again after a brief delay + time.sleep(15) + run_command(app_reg_role_assignment_cmd, + "--role", role, + "--scope", f"/subscriptions/{subscription}") + + log(f"Finished creating service principal: {result}") return result -def create_custom_role(custom_role_name, custom_role_json_local_path, subscription): +def get_or_create_custom_role(custom_role_name, custom_role_json_local_path, subscription): """ Creates a custom role with the given parameters. @@ -302,17 +405,28 @@ def create_custom_role(custom_role_name, custom_role_json_local_path, subscripti Raises: KeyError: If the 'roleName' key is not present in the response from the 'create_custom_role_cmd' command. """ - print("Create custom role") - custom_role = build_custom_role( - custom_role_name, custom_role_json_local_path, subscription - ) - create_custom_role_cmd = "az role definition create" - create_custom_role_response = run_command( - create_custom_role_cmd, "--role-definition", custom_role + get_custom_role_cmd = f'az role definition list --custom-role-only true' + get_custom_role_response = run_command( + get_custom_role_cmd, + "--scope", f"subscriptions/{subscription}", + "--name", custom_role_name ) + role_already_exists = get_custom_role_response is not None and len(get_custom_role_response) > 0 - result = create_custom_role_response["roleName"] - print(f"Finished to create custom role: {result}") + if not role_already_exists: + custom_role = build_custom_role( + custom_role_name, custom_role_json_local_path, subscription + ) + create_custom_role_cmd = "az role definition create" + create_custom_role_response = run_command( + create_custom_role_cmd, "--role-definition", custom_role + ) + + result = create_custom_role_response["roleName"] + log(f"Finished to create custom role: {result}") + else: + result = get_custom_role_response[0]["roleName"] + log(f"Found already existing custom role: {result}") return result @@ -330,11 +444,23 @@ def login_to_azure(): Returns: None """ - print("Please login to azure (Web page should be opened)") + log("Please login to azure (Web page should be opened)") azure_login_cmd = "az login" run_command(azure_login_cmd) +def ensure_azure_cli_automatic_extension_install_enabled(): + """ + Some of the Azure CLI commands used by this script require az cli extensions. + If an extended command is run without extensions installed, the cli will prompt + to install the extension, getting in the way of the script. This configuration + lets the script proceed without prompting, and automatically installs the required + extension. + """ + run_command('az config set', + 'extension.use_dynamic_install=yes_without_prompt') + + def check_azure_cli_installed(): """ Checks if the Azure CLI is installed by running the 'az' command. @@ -352,13 +478,25 @@ def check_azure_cli_installed(): ) +def register_products(spot_account_id, token, products): + session = SpotinstSession(auth_token=token, base_url=SPOT_API_BASE_URL) + client = Client(session=session.session) + if Products.COST_INTELLIGENCE in products: + log(f"Enrolling Spot Account {spot_account_id} in Cost Intelligence.") + + client.send_post(url=SPOT_SETUP_CI_PATH, + body=json.dumps({"account": {"accountId": spot_account_id}}), + entity_name="cost-intelligence-enrollment") + + def main(): parser = argparse.ArgumentParser( description="Create required parameters for Spot registration" ) - parser.add_argument("--subscription", required=True, help="Azure Subscription ID") + parser.add_argument("--subscription", required=False, help="Azure Subscription ID") parser.add_argument("--token", required=True, help="Spot Organization Token") parser.add_argument("--customRoleName", required=True, help="Custom Role Name") + parser.add_argument("--products", required=False, help="Products to register (e.g. core, cost-intelligence)") parser.add_argument( "--customRoleJsonPath", required=False, help="Custom Role Json File Path" ) @@ -367,46 +505,104 @@ def main(): ) args = parser.parse_args() - subscription = args.subscription + subscription_id = args.subscription + if subscription_id is None: + log("Optional argument `--subscription` not specified, defaulting to tenant scope.") + + products = args.products + if products is None: + log("Optional argument `--product` not specified, defaulting to `core` product.") + products = f"{Products.CORE}" + token = args.token - custom_role_name = args.customRoleName + custom_role_name_base = args.customRoleName custom_role_json_local_path = args.customRoleJsonPath if custom_role_json_local_path is None: - print("WARNING: --customRoleJsonPath not found, using default.") - - print(f"Start creating required credentials parameters for Spot registration. Subscription: {subscription}") + log("Optional argument `--customRoleJsonPath` not specified, using recommended custom role definition.") try: check_azure_cli_installed() - # login_to_azure() - subscription_name = get_subscription_name(subscription) - account_id = create_spot_account(subscription_name, token) - service_principal_name = f"Spot-{subscription_name}-{account_id}".replace(" ", "") - required_parameters_response = ( - create_required_parameters_for_spot_registrations( - subscription, - custom_role_name, - custom_role_json_local_path, - service_principal_name, - args.appRegistrationId, - ) - ) - if account_id is not None and token is not None: - set_azure_credentials( - token, - account_id, - required_parameters_response["appId"], - required_parameters_response["password"], - required_parameters_response["tenant"], - subscription, - ) - display_result(subscription, required_parameters_response) - print("Finished creating required credentials parameters for Spot registration") + if subscription_id: + subscription_ids = [subscription_id] + else: + ensure_azure_cli_automatic_extension_install_enabled() + subscription_ids = get_all_subscriptions_in_tenant() + log(f"Found {len(subscription_ids)} subscriptions in tenant.") + + number_successfully_onboarded = 0 + failed_subscriptions = [] + + for index, subscription_id in enumerate(subscription_ids): + try: + subscription_name = "" + subscription_name = get_subscription_name(subscription_id) + log(f"Onboarding subscription {index+1} of {len(subscription_ids)} ({subscription_name} - {subscription_id})") + + account_id = get_or_create_spot_account(subscription_id, subscription_name, token) + custom_role_name = f"{custom_role_name_base}_{account_id}" + + service_principal_name = f"Spot-{subscription_name}-{account_id}".replace(" ", "") + required_parameters_response = ( + create_required_parameters_for_spot_registrations( + subscription_id, + custom_role_name, + custom_role_json_local_path, + service_principal_name, + products, + args.appRegistrationId + ) + ) + if account_id is not None and token is not None: + set_azure_credentials( + token, + account_id, + required_parameters_response["appId"], + required_parameters_response["password"], + required_parameters_response["tenant"], + subscription_id, + ) + + register_products(account_id, token, products) + display_result(subscription_id, required_parameters_response) + + log(f"Completed onboarding subscription {index+1} of {len(subscription_ids)} ({subscription_name} - {subscription_id})\n") + number_successfully_onboarded += 1 + + except SpotinstClientException as e: + # the spotinst token is invalid. halt all onboarding + log(f"Spot client exception. Error: {str(e)}") + failed_subscriptions.append((subscription_id, subscription_name)) + break + except ConnectTimeout as e: + # the Spot API cannot be reached. halt all onboarding + log(f"Spot client connection has timed out. Error: {str(e)}") + failed_subscriptions.append((subscription_id, subscription_name)) + break + except Exception as e: + ''' + a non-transaction breaking exception occurred. stop processing this subscription but continue if + there are more subscriptions to be processed + ''' + log( + f"Error occurred during onboarding process for subscription {subscription_name} - {subscription_id}. Reason: {str(e)}", + LogLevel.ERROR, + ) + failed_subscriptions.append((subscription_id, subscription_name)) + continue + + # print the summary here + log("Operation completed. Summary:") + log(f" Number of subscriptions attempted: {len(subscription_ids)}") + log(f" Number of subscriptions onboarded successfully: {number_successfully_onboarded}") + log(f" Number of subscriptions onboard was unsuccessful: {len(failed_subscriptions)}") + if len(failed_subscriptions) > 0: + log(f" List of subscriptions onboard was unsuccessful:") + for subscription_name, subscription_id in failed_subscriptions: + log(f" {subscription_name} - {subscription_id}") except Exception as e: - print(f"Failed to create required credentials. Errors: {e}") - + log(f"A general error occurred during onboarding. Errors: {e}", LogLevel.ERROR) if __name__ == "__main__": main() From 9e084152d39385e13f26c55a36faf0d90f79ddfa Mon Sep 17 00:00:00 2001 From: Scott Sullivan <126610240+sc0tt-sullivan@users.noreply.github.com> Date: Wed, 3 Apr 2024 07:40:06 -0400 Subject: [PATCH 08/14] Update custom role definition source for core products (#100) * Updating custom role definition source for core products. * Clean up comments. * More cleanup. --- .../azure-automatic-role-assignment.py | 54 ++++++++++++------- Utilities/AzureOnboardingCLI/requirements.txt | 1 + 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py index 7ee6a33..0ad9a48 100644 --- a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py +++ b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py @@ -4,11 +4,10 @@ import re import subprocess import time +import uuid from datetime import datetime import requests -from azure.identity import DefaultAzureCredential -from azure.mgmt.subscription import SubscriptionClient from requests import ConnectTimeout from spotinst_sdk2 import SpotinstSession from spotinst_sdk2.client import Client, SpotinstClientException @@ -16,8 +15,8 @@ CUSTOM_ROLE_NAME = "{customRoleName}" SUBSCRIPTION_ID = "{subscriptionId}" -CUSTOM_ROLE_PERMISSION_URL = ( - "https://nirtest2.s3-us-west-2.amazonaws.com/custom_role_file.json" +CORE_PRODUCT_CUSTOM_ROLE_URL = ( + "https://spotinst-public.s3.amazonaws.com/assets/azure/custom_role_file.json" ) HTTP_OK_RESPONSE_STATUSES = range(200, 300) SPOT_API_BASE_URL = 'https://api.spotinst.io' @@ -38,6 +37,11 @@ class LogLevel: INFO = "INFO" +# Pass through argument to subprocess.run(). See documentation there for additional information. When running in Windows +# and seeing errors executing commands, it may be helpful to use `shell=True`. +run_in_shell = False + + def log(message, log_level=LogLevel.INFO): print(f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]} [{log_level}] {message}") @@ -62,11 +66,10 @@ def get_subscription_name(subscription_id): Returns: str: The display name of the subscription. """ - credential = DefaultAzureCredential() - subscription_client = SubscriptionClient(credential) - subscription = subscription_client.subscriptions.get(subscription_id) + subscription_display_name = run_command( + f'az account subscription show --subscription-id {subscription_id} --query displayName') - return subscription.display_name + return subscription_display_name def get_or_create_spot_account(subscription_id, name, token): @@ -137,17 +140,9 @@ def run_command(cmd, *args): cmd_to_run = cmd_args + list(args) run_process_result = subprocess.run( - cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8" + cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8", shell=run_in_shell ) # Maybe need to change to check output because in python there isn't encoding - # try: - # output = subprocess.check_output( - # cmnd, stderr=subprocess.STDOUT) - # except subprocess.CalledProcessError as exc: - # print("Status : FAIL", exc.returncode, exc.output) - # else: - # print(f"Output: \n{output}\n") - if run_process_result.returncode != 0: raise Exception( f"Failed to run the command: {cmd}. Errors: {run_process_result.stderr}" @@ -247,7 +242,7 @@ def build_custom_role(custom_role_name, custom_role_json_local_path, subscriptio .replace(CUSTOM_ROLE_NAME, custom_role_name) ) else: - get_custom_role_from_s3_response = requests.get(CUSTOM_ROLE_PERMISSION_URL) + get_custom_role_from_s3_response = requests.get(CORE_PRODUCT_CUSTOM_ROLE_URL) if get_custom_role_from_s3_response.status_code in HTTP_OK_RESPONSE_STATUSES: result = ( @@ -417,12 +412,26 @@ def get_or_create_custom_role(custom_role_name, custom_role_json_local_path, sub custom_role = build_custom_role( custom_role_name, custom_role_json_local_path, subscription ) + + # the spot core product custom role is defined in the REST API format (slightly different from the CLI format) + # so use the az rest command + custom_role_id = uuid.uuid4() + create_custom_role_cmd = "az rest --method put" + create_custom_role_response = run_command( + create_custom_role_cmd, + "--url", f"https://management.azure.com/subscriptions/{subscription}/providers/Microsoft.Authorization/roleDefinitions/{custom_role_id}?api-version=2022-04-01", + "--body", custom_role + ) + result = create_custom_role_response["properties"]["roleName"] + + ''' If using a custom role in the CLI format, use the `az role definition create` command create_custom_role_cmd = "az role definition create" create_custom_role_response = run_command( create_custom_role_cmd, "--role-definition", custom_role ) - result = create_custom_role_response["roleName"] + ''' + log(f"Finished to create custom role: {result}") else: result = get_custom_role_response[0]["roleName"] @@ -470,7 +479,7 @@ def check_azure_cli_installed(): """ az_cmd = "az" exit_code = subprocess.call( - az_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + az_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=run_in_shell ) if exit_code != 0: raise Exception( @@ -503,8 +512,13 @@ def main(): parser.add_argument( "--appRegistrationId", required=False, help="Existing App Registration ID" ) + parser.add_argument('--shell', default=False, action='store_true', + help="The command will be executed through the shell. May be helpful if seeing errors when running in Windows.") args = parser.parse_args() + global run_in_shell + run_in_shell = args.shell + subscription_id = args.subscription if subscription_id is None: log("Optional argument `--subscription` not specified, defaulting to tenant scope.") diff --git a/Utilities/AzureOnboardingCLI/requirements.txt b/Utilities/AzureOnboardingCLI/requirements.txt index bf90e8b..1cd86c1 100644 --- a/Utilities/AzureOnboardingCLI/requirements.txt +++ b/Utilities/AzureOnboardingCLI/requirements.txt @@ -23,3 +23,4 @@ six==1.16.0 spotinst-sdk2==2.1.38 typing_extensions==4.7.1 urllib3==2.0.6 +uuid==1.30 From b293eacd0e147152588c765735d3cb562e9e11b8 Mon Sep 17 00:00:00 2001 From: Hozefa Bata Date: Wed, 3 Apr 2024 15:40:25 -0500 Subject: [PATCH 09/14] Update Spot-AWS-Restricted-Full-Permissions.json (#101) We do required the following to get allow STS iam:PassRole --- Policies/AWS/Spot-AWS-Restricted-Full-Permissions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Policies/AWS/Spot-AWS-Restricted-Full-Permissions.json b/Policies/AWS/Spot-AWS-Restricted-Full-Permissions.json index 03094a1..4140614 100644 --- a/Policies/AWS/Spot-AWS-Restricted-Full-Permissions.json +++ b/Policies/AWS/Spot-AWS-Restricted-Full-Permissions.json @@ -112,6 +112,7 @@ "iam:GetPolicyVersion", "iam:ListPolicies", "iam:GetPolicy", + "iam:PassRole", "iam:ListAttachedRolePolicies", "organizations:ListAccounts", "iam:CreateServiceLinkedRole" From 7d4f7cd9a07366c0686ad777df156295e71cd68a Mon Sep 17 00:00:00 2001 From: Scott Sullivan <126610240+sc0tt-sullivan@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:16:32 -0400 Subject: [PATCH 10/14] SF-10715 - Updating Azure onboarding script to allow for skipping azure resource creation (#102) --- .../azure-automatic-role-assignment.py | 278 ++++++++++-------- 1 file changed, 159 insertions(+), 119 deletions(-) diff --git a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py index 0ad9a48..c2a7113 100644 --- a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py +++ b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py @@ -48,7 +48,7 @@ def log(message, log_level=LogLevel.INFO): def get_all_subscriptions_in_tenant(): """ - Retrieves all the subscriptions in the tenant (that the use can access). + Retrieves all the subscriptions in the tenant. Returns: list(str): The subscription ids in the tenant. """ @@ -56,6 +56,16 @@ def get_all_subscriptions_in_tenant(): return subscription_ids +def get_active_tenant(): + """ + Gets the active tenant. + Returns: + str: The tenant id. + """ + tenant_id = run_command("az account show --query tenantId") + return tenant_id + + def get_subscription_name(subscription_id): """ Retrieves the display name of a subscription based on the given subscription ID. @@ -160,22 +170,24 @@ def run_command(cmd, *args): return result -def display_result(subscription, create_or_get_service_principal_response): +def display_app_result(subscription_id, tenant_id, app_registration_id, client_secret): """ - Display the result of the subscription and create/get service principal response. + Display the result of the subscription and service principal. Args: - subscription (str): The subscription ID. - create_or_get_service_principal_response (dict): The response from the create or get service principal API. + subscription_id (str): The subscription id. + tenant_id (str): The tenant id. + app_registration_id (str): The application registration id associated with the service principal. + client_secret (str): The client secret associated with the service principal. Returns: None """ log("Your credentials details:") - log(f"Application (client) ID: {create_or_get_service_principal_response['appId']}") - log(f"Client Secret: {create_or_get_service_principal_response['password']}") - log(f"Tenant ID: {create_or_get_service_principal_response['tenant']}") - log(f"Subscription ID: {subscription}") + log(f"Application Registration ID: {app_registration_id}") + log(f"Client Secret: {client_secret}") + log(f"Tenant ID: {tenant_id}") + log(f"Subscription ID: {subscription_id}") def set_azure_credentials( @@ -254,68 +266,66 @@ def build_custom_role(custom_role_name, custom_role_json_local_path, subscriptio return result -def create_required_parameters_for_spot_registrations( - subscription, - custom_role_name, - custom_role_json_local_path, - service_principal_name, - products, - app_registration_id=None -): - if does_subscription_exist_for_account(subscription): - roles = [] - if Products.CORE in products: - custom_role_name = get_or_create_custom_role( - custom_role_name, custom_role_json_local_path, subscription - ) - roles.append(custom_role_name) - - if Products.COST_INTELLIGENCE in products: - # cost intelligence requires the Azure built-in READER role - roles.append(BuiltInAzureRoles.READER) - - if app_registration_id is not None: - create_or_get_service_principal_response = get_service_principal( - app_registration_id, roles, subscription, service_principal_name - ) - else: - create_or_get_service_principal_response = create_service_principal( - roles, service_principal_name, subscription - ) - - result = create_or_get_service_principal_response - else: - raise Exception("Could not find the subscription in account subscriptions") +def get_roles_for_products(products, custom_role_name, custom_role_json_local_path, subscription): + roles = [] + if Products.CORE in products: + custom_role_name = get_or_create_custom_role( + custom_role_name, custom_role_json_local_path, subscription + ) + roles.append(custom_role_name) - return result + if Products.COST_INTELLIGENCE in products: + # cost intelligence requires the Azure built-in READER role + roles.append(BuiltInAzureRoles.READER) + + return roles -def get_service_principal(app_registration_id, roles, subscription, credential_name): +def create_client_secret(app_registration_id, credential_name): """ - Generate a service principal for the given app registration ID, assign a custom role to the service principal, create a secret for the app registration, and return the result of creating the secret. + Create a secret for the App Registration Parameters: - app_registration_id (str): The ID of the app registration. - - roles (list(str)): The names of the roles to assign to the service principal. - - subscription (str): The ID of the subscription. - credential_name (str): The display name to use for the credential. Returns: - create_secret_result (dict): The result of creating the secret for the app registration. """ - app_reg_show_cmd = f"az ad sp show --id {app_registration_id}" - run_command(app_reg_show_cmd) + create_secret_cmd = f"az ad app credential reset --id {app_registration_id} --append --display-name {credential_name}" + result = run_command(create_secret_cmd) - # Assign the roles to the service principal - for role in roles: - app_reg_role_assignment_cmd = f"az role assignment create --assignee {app_registration_id} --role {role} --scope /subscriptions/{subscription}" - run_command(app_reg_role_assignment_cmd) + return (result["appId"], result["password"], result["tenant"]) - # Create a secret for the App Registration - create_secret_cmd = f"az ad app credential reset --id {app_registration_id} --append --display-name {credential_name}" - create_secret_result = run_command(create_secret_cmd) - return create_secret_result +def assign_roles(app_registration_id, roles, subscription): + """ + Assign the roles to the service principal. + + Parameters: + - app_registration_id (str): The app registration id for the service principal. + see https://learn.microsoft.com/en-us/cli/azure/role/assignment?view=azure-cli-latest#az-role-assignment-create + - roles (list(str)): The names of the roles to assign to the service principal. + - subscription (str): The ID of the subscription. + """ + # Get the object_id for the service principal. This is used with --assignee-object-id to avoid errors caused by propagation latency in AAD Graph. + # see https://learn.microsoft.com/en-us/cli/azure/role/assignment?view=azure-cli-latest#az-role-assignment-create + get_app_object_id_cmd = f"az ad sp show --id {app_registration_id}" + object_id_result = run_command(get_app_object_id_cmd) + app_object_id = object_id_result.get('id') + + for role in roles: + app_reg_role_assignment_cmd = f"az role assignment create --assignee-object-id {app_object_id} --assignee-principal-type ServicePrincipal" + try: + run_command(app_reg_role_assignment_cmd, + "--role", role, + "--scope", f"/subscriptions/{subscription}") + except: + # the role assignment may have failed because of propagation latency. try again after a brief delay + time.sleep(15) + run_command(app_reg_role_assignment_cmd, + "--role", role, + "--scope", f"/subscriptions/{subscription}") def does_subscription_exist_for_account(subscription): @@ -341,53 +351,33 @@ def does_subscription_exist_for_account(subscription): return False -def create_service_principal(roles, service_principal_name, subscription): +def create_service_principal(service_principal_name): """ - Creates a service principal with the given custom role name, service principal name, and subscription. + Creates a new app registration and corresponding service principal. Args: - roles (list(str)): The names of the roles to assign to the service principal. service_principal_name (str): The name of the service principal. - subscription (str): The subscription ID. Returns: - str: The result of creating the service principal. + (str1, str2, str3) where: + str1: The ID of the app registration. + str2: The client secret that is the result of creating the service principal + str3: The tenant id where app registration and service principal reside """ log("Creating service principal") create_service_principal_cmd = f"az ad sp create-for-rbac" result = run_command(create_service_principal_cmd, "--name", service_principal_name) - app_registration_id = result.get('appId') - - # Get the object_id for the service principal. This is used with --assignee-object-id to avoid errors caused by propagation latency in AAD Graph. - # see https://learn.microsoft.com/en-us/cli/azure/role/assignment?view=azure-cli-latest#az-role-assignment-create - get_app_object_id_cmd = f"az ad sp show --id {app_registration_id}" - object_id_result = run_command(get_app_object_id_cmd) - app_object_id = object_id_result.get('id') - - # Assign the roles to the service principal - for role in roles: - app_reg_role_assignment_cmd = f"az role assignment create --assignee-object-id {app_object_id} --assignee-principal-type ServicePrincipal" - try: - run_command(app_reg_role_assignment_cmd, - "--role", role, - "--scope", f"/subscriptions/{subscription}") - except: - # the role assignment may have failed because of propagation latency. try again after a brief delay - time.sleep(15) - run_command(app_reg_role_assignment_cmd, - "--role", role, - "--scope", f"/subscriptions/{subscription}") log(f"Finished creating service principal: {result}") - return result + return (result["appId"], result["password"], result["tenant"]) def get_or_create_custom_role(custom_role_name, custom_role_json_local_path, subscription): """ - Creates a custom role with the given parameters. + Creates a custom role with the given parameters if the role does not already exist. Args: custom_role_name (str): The name of the custom role. @@ -488,6 +478,13 @@ def check_azure_cli_installed(): def register_products(spot_account_id, token, products): + """ + Enrolls the spot account in the specified Spot products by communicating with the Spot api. + Args: + spot_account_id: The Spot account to enroll products for. + token: The Spot api token. + products: A string representing which products to enroll in. + """ session = SpotinstSession(auth_token=token, base_url=SPOT_API_BASE_URL) client = Client(session=session.session) if Products.COST_INTELLIGENCE in products: @@ -498,27 +495,53 @@ def register_products(spot_account_id, token, products): entity_name="cost-intelligence-enrollment") -def main(): +def parse_args(): + """ + Retrieve the command line arguments. + """ parser = argparse.ArgumentParser( description="Create required parameters for Spot registration" ) - parser.add_argument("--subscription", required=False, help="Azure Subscription ID") - parser.add_argument("--token", required=True, help="Spot Organization Token") - parser.add_argument("--customRoleName", required=True, help="Custom Role Name") + + # Target Azure subscriptions + parser.add_argument("--subscription", required=False, help="Azure Subscription ID to connect to Spot account. If unspecified all subscriptions in current tenant will be onboarded.") + + # Spot configuration + parser.add_argument("--token", required=True, help="Spot organization token.") parser.add_argument("--products", required=False, help="Products to register (e.g. core, cost-intelligence)") - parser.add_argument( - "--customRoleJsonPath", required=False, help="Custom Role Json File Path" - ) - parser.add_argument( - "--appRegistrationId", required=False, help="Existing App Registration ID" - ) + + # Azure resource configuration + parser.add_argument("--skipResourceCreation", default=False, action="store_true", help="Skip creating any resources in Azure. Only creates Spot resources.") + parser.add_argument("--customRoleName", default="Spot-CoreRole", required=False, help="Name to use for the custom role.") + parser.add_argument("--customRoleJsonPath", required=False, help="Custom role Json file path. If specified will not use the default role.") + parser.add_argument("--appRegistrationId", required=False, help="Existing App Registration ID. If specified will not create a new app registration.") + parser.add_argument("--clientSecret", required=False, help="Client secret for the app registration. If specified will not create a new client secret.") + + # other options parser.add_argument('--shell', default=False, action='store_true', help="The command will be executed through the shell. May be helpful if seeing errors when running in Windows.") + args = parser.parse_args() + # custom argument validation + if args.skipResourceCreation and not (args.appRegistrationId and args.clientSecret): + parser.error("Argument --skipResourceCreation requires --appRegistrationId and --clientSecret to be specified.") + + return args + + +def main(): + # command line arguments + args = parse_args() + + # shell configuration global run_in_shell run_in_shell = args.shell + # assign args + if args.skipResourceCreation: + log("Argument `--skipResourceCreation` specified, no Azure resources will be created.") + subscription_id = args.subscription if subscription_id is None: log("Optional argument `--subscription` not specified, defaulting to tenant scope.") @@ -528,16 +551,21 @@ def main(): log("Optional argument `--product` not specified, defaulting to `core` product.") products = f"{Products.CORE}" - token = args.token custom_role_name_base = args.customRoleName custom_role_json_local_path = args.customRoleJsonPath - if custom_role_json_local_path is None: + if not args.skipResourceCreation and custom_role_json_local_path is None: log("Optional argument `--customRoleJsonPath` not specified, using recommended custom role definition.") + token = args.token + app_registration_id = args.appRegistrationId + client_secret = args.clientSecret + try: check_azure_cli_installed() + # Subscriptions / tenant + tenant_id = get_active_tenant() if subscription_id: subscription_ids = [subscription_id] else: @@ -545,41 +573,53 @@ def main(): subscription_ids = get_all_subscriptions_in_tenant() log(f"Found {len(subscription_ids)} subscriptions in tenant.") + # App registration + if not args.skipResourceCreation: + service_principal_name = "Spot-App" + should_create_app = args.appRegistrationId is None + if should_create_app: + # create the app registration + (app_registration_id, client_secret, tenant) = create_service_principal(service_principal_name) + else: + # if the client secret was not supplied, create a new one + should_reset_client_secret = client_secret is None + if should_reset_client_secret: + (app_registration_id, client_secret, tenant) = create_client_secret(app_registration_id, service_principal_name) + number_successfully_onboarded = 0 failed_subscriptions = [] + # process each subscription specified for index, subscription_id in enumerate(subscription_ids): try: - subscription_name = "" subscription_name = get_subscription_name(subscription_id) log(f"Onboarding subscription {index+1} of {len(subscription_ids)} ({subscription_name} - {subscription_id})") account_id = get_or_create_spot_account(subscription_id, subscription_name, token) - custom_role_name = f"{custom_role_name_base}_{account_id}" - - service_principal_name = f"Spot-{subscription_name}-{account_id}".replace(" ", "") - required_parameters_response = ( - create_required_parameters_for_spot_registrations( - subscription_id, - custom_role_name, - custom_role_json_local_path, - service_principal_name, - products, - args.appRegistrationId - ) + + # only create Azure resources (app registration, client secret, custom role, et al.) if specified + if not args.skipResourceCreation: + custom_role_name = f"{custom_role_name_base}_{account_id}" + roles = get_roles_for_products(products, custom_role_name, custom_role_json_local_path, subscription_id) + + # assign the roles to the service principal + assign_roles(app_registration_id, roles, subscription_id) + + # set the credentials in Spot + set_azure_credentials( + token, + account_id, + app_registration_id, + client_secret, + tenant_id, + subscription_id, ) - if account_id is not None and token is not None: - set_azure_credentials( - token, - account_id, - required_parameters_response["appId"], - required_parameters_response["password"], - required_parameters_response["tenant"], - subscription_id, - ) + # register the selected products in Spot register_products(account_id, token, products) - display_result(subscription_id, required_parameters_response) + + if not args.clientSecret: + display_app_result(subscription_id, tenant_id, app_registration_id, client_secret) log(f"Completed onboarding subscription {index+1} of {len(subscription_ids)} ({subscription_name} - {subscription_id})\n") number_successfully_onboarded += 1 From 7fceade23e5a193e02b20b38581d486ffafb702b Mon Sep 17 00:00:00 2001 From: wesctl Date: Thu, 18 Jul 2024 10:59:02 -0400 Subject: [PATCH 11/14] azure bulk import py updates and gcp bulkimporter cloudshell (#104) * add ListAccountAliases to EKS StackSet * remove buggy output code * add gc onboarder script * fix format * format fix * fix format * fix formatting * fix wording * fix url * fix content * fix content * update readme * fix examples * update read-only default to false * download role once * remove spot_account_id * grammar fixes * add csv list option for subscription onboarding * add file based onboarding * remove unused import * clean up comments --- .../azure-automatic-role-assignment.py | 14 ++- .../GC/gc-cloudshell-bulk-import/readme.md | 59 ++++++++++++ .../spot_onboarder.sh | 94 +++++++++++++++++++ 3 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 Utilities/GC/gc-cloudshell-bulk-import/readme.md create mode 100755 Utilities/GC/gc-cloudshell-bulk-import/spot_onboarder.sh diff --git a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py index c2a7113..699f138 100644 --- a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py +++ b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py @@ -505,6 +505,7 @@ def parse_args(): # Target Azure subscriptions parser.add_argument("--subscription", required=False, help="Azure Subscription ID to connect to Spot account. If unspecified all subscriptions in current tenant will be onboarded.") + parser.add_argument("--subscriptionFileName", required=False, help="File path to txt list of subscriptions to connect to Spot accounts.") # Spot configuration parser.add_argument("--token", required=True, help="Spot organization token.") @@ -543,9 +544,15 @@ def main(): log("Argument `--skipResourceCreation` specified, no Azure resources will be created.") subscription_id = args.subscription + if subscription_id is None: + log("Optional argument `--subscription` not specified, looking for txt list or will default to tenant scope.") + if args.subscriptionFileName != "": + subscription_id_list_file_name = args.subscriptionFileName + log("will look for txt file in local path and try to open") + if subscription_id is None: log("Optional argument `--subscription` not specified, defaulting to tenant scope.") - + products = args.products if products is None: log("Optional argument `--product` not specified, defaulting to `core` product.") @@ -568,6 +575,11 @@ def main(): tenant_id = get_active_tenant() if subscription_id: subscription_ids = [subscription_id] + elif subscription_id_list_file_name: + f = open(subscription_id_list_file_name, 'r') + data = f.read() + subscription_ids = str.split(data,"\n") + f.close() else: ensure_azure_cli_automatic_extension_install_enabled() subscription_ids = get_all_subscriptions_in_tenant() diff --git a/Utilities/GC/gc-cloudshell-bulk-import/readme.md b/Utilities/GC/gc-cloudshell-bulk-import/readme.md new file mode 100644 index 0000000..6ec8ddc --- /dev/null +++ b/Utilities/GC/gc-cloudshell-bulk-import/readme.md @@ -0,0 +1,59 @@ + +# Spot.io GC Cloudshell Bulk Onboarder + + +Spot.io GC Cloudshell Bulk Onboarder is a bulk project import tool that can be ran directly in the GC console terminal. It creates Spot.io required GC roles, service accounts, a Spot.io token per project and will onboard GC projects to the Spot console using the Spot.io API and gcloud commands. + + +## Prerequisites +* An active Spot.io account is required with a provisioned API admin token. See [here](https://docs.spot.io/administration/api/create-api-token) if you need to setup a token. +* GC Projects must have the Compute Engine API enabled. You can check the status for each project using the following url. https://console.developers.google.com/apis/api/compute.googleapis.com/overview?project=#####insert_project_name###### +* A single, comma separated string of GC Projects that needs onboarding. +* GC Admin access to all target projects and access to the GC cloudshell console + +## Limitations and considerations +* There is no rollback feature for this tool. Duplicate assets will be generated if the script is run more than once to onboard the same project. All duplicate assets created in GC will need to be cleaned up manually. +* Once the GC read-only Roles have been deployed; this tool cannot upgrade deployed Role policies to full permissions at a later date. + +## Automation Flow +All assets created by the tool are on a per project basis for security purposes. Each connected project will have a unique Spot.io token restricted to the matching GC service account. + +. Iterate through the input list of projects\ +.. Create GC Service Account\ +... Create and download Service Account Key\ +.... Create Spot.io account\ +..... Create unique programmatic user API token\ +...... Import GC project using the downloaded Service Account Key, Spot.io Acct ID and unique programmatic API user token\ +(loop) + +## Deployment +To deploy this script run the following shell commands + +```bash + wget https://s3-url-tbd/spot_onboarder.sh + chmod +x spot_onboarder.sh +``` + +## Script Usage Example +```bash + + ./spot_onboarder.sh api-token-value csv-list-of-projects false + +``` + +## Logging +The script will generate a lot of log prints. It would be best if you pipe them to a log file for follow-up analysis. + +```bash + + ./spot_onboarder.sh api-token-value csv-list-of-projects false >> spot_onboarder.log + +``` + + +## Input Parameters +* (String) API token +* (String) CSV list of GC Projects +* (boolean) Read-only Role deployment flag - Default: false + + diff --git a/Utilities/GC/gc-cloudshell-bulk-import/spot_onboarder.sh b/Utilities/GC/gc-cloudshell-bulk-import/spot_onboarder.sh new file mode 100755 index 0000000..ff5a982 --- /dev/null +++ b/Utilities/GC/gc-cloudshell-bulk-import/spot_onboarder.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +# Function to create a service account and download its key; Create and connect GCP project to Spot.io account using a programmatic user token +# Parameters: +# 1. spot_token: The Spot.io token. No default value. +# 2. project_ids: A comma-separated list of project IDs. No default value. +# 3. read_only: A boolean indicating whether the account should be read-only. Defaults to false. +create_service_account() { + local service_account_name="spot-io-$(openssl rand -hex 4)" + local spot_role_name="spot_io_Role_$(openssl rand -hex 4)" + local spot_token=$1 + local project_ids=($(echo $2 | tr "," "\n")) + local read_only=${3:-false} + local ROLE_YML="spotinst_service_account_role.yml" + local ROLE_URI="https://spotinst-public.s3.amazonaws.com/assets/gcp/spotinst-service-role.yaml" + + if [ "$read_only" = true ] ; then + ROLE_URI="https://spot-connect-account-cf.s3.amazonaws.com/spot-gcp-service-role-read-only.yaml" + fi + + # Downloading spotinst-service-role.yml to local folder + curl ${ROLE_URI} -o ${ROLE_YML} + + if [ -z "$spot_token" ]; then + echo "Spot Token: ${spot_token}" + exit -1 + fi + + for project_id in "${project_ids[@]}" + do + # enable service management API + gcloud services enable servicemanagement --project ${project_id} + # create spotinst role from file + gcloud iam roles create ${spot_role_name} --project ${project_id} --file ${ROLE_YML} -q + # create spotinst ServiceAccount + gcloud iam service-accounts create ${service_account_name} --display-name ${service_account_name} --project ${project_id} + # add spotinst role to spotinst service account + gcloud projects add-iam-policy-binding ${project_id} \ + --member serviceAccount:${service_account_name}@${project_id}.iam.gserviceaccount.com \ + --role projects/${project_id}/roles/${spot_role_name} --condition=None + # add serviceAccountUser role to spotinst service account + gcloud projects add-iam-policy-binding ${project_id} \ + --member serviceAccount:${service_account_name}@${project_id}.iam.gserviceaccount.com \ + --role roles/iam.serviceAccountUser --condition=None + # Create and download key for service account + gcloud iam service-accounts keys create spotinst_key.json \ + --iam-account ${service_account_name}@${project_id}.iam.gserviceaccount.com --project ${project_id} + + # Call Spot.io API to set existing user permissions + response=$(curl -X POST "https://api.spotinst.io/setup/account" \ + -H "Authorization: Bearer $spot_token" \ + -H "Content-Type: application/json" \ + -d "{ + \"account\": { + \"name\": \"GCP-$project_id\" + } + }") + + # Extract the account id from the response and set it to new_spot_account_id + new_spot_account_id=$(echo $response | jq -r '.response.items[0].id') + + # Call Spot.io API to create a programmatic user + response=$(curl -X POST "https://api.spotinst.io/setup/user/programmatic" \ + -H "Authorization: Bearer $spot_token" \ + -H "Content-Type: application/json" \ + -d "{ + \"name\": \"GCP-$project_id-$(openssl rand -hex 4)\", + \"accounts\": [ + { + \"id\": \"$new_spot_account_id\", + \"role\": \"editor\" + } + ] + }") + + # Extract the token from the response and update the Spot.io token + programmatic_spot_token=$(echo $response | jq -r '.response.items[0].token') + + # Echo the Spot.io token to the console + echo "The Spot.io token for the programmatic user is: $programmatic_spot_token" + + # Call Spot.io API to setup credentials + service_account_key_data=$(cat spotinst_key.json | jq 'del(.universe_domain)') + curl -X POST "https://api.spotinst.io/gcp/setup/credentials/?accountId=$new_spot_account_id" \ + -H "Authorization: Bearer $programmatic_spot_token" \ + -H "Content-Type: application/json" \ + -d "{ + \"serviceAccount\": $service_account_key_data + }" + done +} + +# Call the function with command line arguments +create_service_account "$@" From ec7fbd17d542f50322443f11a76d9d237ee4db7b Mon Sep 17 00:00:00 2001 From: Scott Sullivan <126610240+sc0tt-sullivan@users.noreply.github.com> Date: Thu, 12 Sep 2024 09:33:56 -0400 Subject: [PATCH 12/14] SF-14969 - Updating Azure onboarding script to include permissions for Spot Security product. (#105) --- .../AzureOnboardingCLI/azure-automatic-role-assignment.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py index 699f138..d578603 100644 --- a/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py +++ b/Utilities/AzureOnboardingCLI/azure-automatic-role-assignment.py @@ -26,10 +26,12 @@ class Products: CORE = 'core' COST_INTELLIGENCE = 'cost-intelligence' + SPOT_SECURITY = 'spot-security' class BuiltInAzureRoles: READER = 'READER' + STORAGE_BLOB_DATA_READER = 'Storage Blob Data Reader' class LogLevel: @@ -277,6 +279,9 @@ def get_roles_for_products(products, custom_role_name, custom_role_json_local_pa if Products.COST_INTELLIGENCE in products: # cost intelligence requires the Azure built-in READER role roles.append(BuiltInAzureRoles.READER) + + if Products.SPOT_SECURITY in products: + roles.append(BuiltInAzureRoles.STORAGE_BLOB_DATA_READER) return roles @@ -509,7 +514,7 @@ def parse_args(): # Spot configuration parser.add_argument("--token", required=True, help="Spot organization token.") - parser.add_argument("--products", required=False, help="Products to register (e.g. core, cost-intelligence)") + parser.add_argument("--products", required=False, help="Products to register (e.g. core, cost-intelligence, spot-security, etc.)") # Azure resource configuration parser.add_argument("--skipResourceCreation", default=False, action="store_true", help="Skip creating any resources in Azure. Only creates Spot resources.") From 22aec43e4fc362d4ee1983e984b83dc43735251a Mon Sep 17 00:00:00 2001 From: Jeff Snyder Date: Wed, 28 May 2025 10:00:09 -0400 Subject: [PATCH 13/14] [src] SF-21801: Add Eco setup scripts. (#106) * [src] SF-21801: Add Eco terraform setup scripts. * [src] SF-21801: Add Eco onboarding setup scripts. * [src] SF-21801: Remove unused policies for Eco. --- .../Azure/Spot-Azure-ECO-Full-Permission.json | 94 ------------- Policies/Azure/Spot-Azure-ECO-ReadOnly.json | 82 ----------- .../AzureSetup/CSP/eco_azure_full_access.ps1 | 51 +++++++ .../AzureSetup/CSP/eco_azure_full_access.py | 132 ++++++++++++++++++ .../AzureSetup/CSP/eco_azure_full_access.sh | 46 ++++++ .../AzureSetup/CSP/eco_azure_full_access.tf | 103 ++++++++++++++ .../AzureSetup/CSP/eco_azure_readonly.ps1 | 40 ++++++ .../AzureSetup/CSP/eco_azure_readonly.py | 82 +++++++++++ .../AzureSetup/CSP/eco_azure_readonly.sh | 34 +++++ .../AzureSetup/CSP/eco_azure_readonly.tf | 75 ++++++++++ .../AzureSetup/EA/eco_azure_full_access.ps1 | 52 +++++++ .../AzureSetup/EA/eco_azure_full_access.py | 132 ++++++++++++++++++ .../AzureSetup/EA/eco_azure_full_access.sh | 47 +++++++ .../AzureSetup/EA/eco_azure_full_access.tf | 103 ++++++++++++++ .../AzureSetup/EA/eco_azure_readonly.ps1 | 40 ++++++ Utilities/AzureSetup/EA/eco_azure_readonly.py | 80 +++++++++++ Utilities/AzureSetup/EA/eco_azure_readonly.sh | 34 +++++ Utilities/AzureSetup/EA/eco_azure_readonly.tf | 75 ++++++++++ .../AzureSetup/MCA/eco_azure_full_access.ps1 | 52 +++++++ .../AzureSetup/MCA/eco_azure_full_access.py | 132 ++++++++++++++++++ .../AzureSetup/MCA/eco_azure_full_access.sh | 46 ++++++ .../AzureSetup/MCA/eco_azure_full_access.tf | 103 ++++++++++++++ .../AzureSetup/MCA/eco_azure_readonly.ps1 | 40 ++++++ .../AzureSetup/MCA/eco_azure_readonly.py | 80 +++++++++++ .../AzureSetup/MCA/eco_azure_readonly.sh | 34 +++++ .../AzureSetup/MCA/eco_azure_readonly.tf | 75 ++++++++++ .../AzureSetup/PAYG/eco_azure_full_access.ps1 | 48 +++++++ .../AzureSetup/PAYG/eco_azure_full_access.py | 118 ++++++++++++++++ .../AzureSetup/PAYG/eco_azure_full_access.sh | 42 ++++++ .../AzureSetup/PAYG/eco_azure_full_access.tf | 96 +++++++++++++ .../AzureSetup/PAYG/eco_azure_readonly.ps1 | 36 +++++ .../AzureSetup/PAYG/eco_azure_readonly.py | 66 +++++++++ .../AzureSetup/PAYG/eco_azure_readonly.sh | 30 ++++ .../AzureSetup/PAYG/eco_azure_readonly.tf | 68 +++++++++ Utilities/AzureSetup/README.md | 33 +++++ Utilities/AzureSetup/requirements.txt | 2 + 36 files changed, 2227 insertions(+), 176 deletions(-) delete mode 100644 Policies/Azure/Spot-Azure-ECO-Full-Permission.json delete mode 100644 Policies/Azure/Spot-Azure-ECO-ReadOnly.json create mode 100644 Utilities/AzureSetup/CSP/eco_azure_full_access.ps1 create mode 100644 Utilities/AzureSetup/CSP/eco_azure_full_access.py create mode 100644 Utilities/AzureSetup/CSP/eco_azure_full_access.sh create mode 100644 Utilities/AzureSetup/CSP/eco_azure_full_access.tf create mode 100644 Utilities/AzureSetup/CSP/eco_azure_readonly.ps1 create mode 100644 Utilities/AzureSetup/CSP/eco_azure_readonly.py create mode 100644 Utilities/AzureSetup/CSP/eco_azure_readonly.sh create mode 100644 Utilities/AzureSetup/CSP/eco_azure_readonly.tf create mode 100644 Utilities/AzureSetup/EA/eco_azure_full_access.ps1 create mode 100644 Utilities/AzureSetup/EA/eco_azure_full_access.py create mode 100644 Utilities/AzureSetup/EA/eco_azure_full_access.sh create mode 100644 Utilities/AzureSetup/EA/eco_azure_full_access.tf create mode 100644 Utilities/AzureSetup/EA/eco_azure_readonly.ps1 create mode 100644 Utilities/AzureSetup/EA/eco_azure_readonly.py create mode 100644 Utilities/AzureSetup/EA/eco_azure_readonly.sh create mode 100644 Utilities/AzureSetup/EA/eco_azure_readonly.tf create mode 100644 Utilities/AzureSetup/MCA/eco_azure_full_access.ps1 create mode 100644 Utilities/AzureSetup/MCA/eco_azure_full_access.py create mode 100644 Utilities/AzureSetup/MCA/eco_azure_full_access.sh create mode 100644 Utilities/AzureSetup/MCA/eco_azure_full_access.tf create mode 100644 Utilities/AzureSetup/MCA/eco_azure_readonly.ps1 create mode 100644 Utilities/AzureSetup/MCA/eco_azure_readonly.py create mode 100644 Utilities/AzureSetup/MCA/eco_azure_readonly.sh create mode 100644 Utilities/AzureSetup/MCA/eco_azure_readonly.tf create mode 100644 Utilities/AzureSetup/PAYG/eco_azure_full_access.ps1 create mode 100644 Utilities/AzureSetup/PAYG/eco_azure_full_access.py create mode 100644 Utilities/AzureSetup/PAYG/eco_azure_full_access.sh create mode 100644 Utilities/AzureSetup/PAYG/eco_azure_full_access.tf create mode 100644 Utilities/AzureSetup/PAYG/eco_azure_readonly.ps1 create mode 100644 Utilities/AzureSetup/PAYG/eco_azure_readonly.py create mode 100644 Utilities/AzureSetup/PAYG/eco_azure_readonly.sh create mode 100644 Utilities/AzureSetup/PAYG/eco_azure_readonly.tf create mode 100644 Utilities/AzureSetup/README.md create mode 100644 Utilities/AzureSetup/requirements.txt diff --git a/Policies/Azure/Spot-Azure-ECO-Full-Permission.json b/Policies/Azure/Spot-Azure-ECO-Full-Permission.json deleted file mode 100644 index babcdc1..0000000 --- a/Policies/Azure/Spot-Azure-ECO-Full-Permission.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "properties": { - "roleName": "Spot Eco", - "description": "Spot Eco full permission role to be granted to Spot Eco users to each subscription to enable automation", - "assignableScopes": ["/subscriptions/"], - "permissions": [ - { - "actions": [ - "Microsoft.Authorization/roleAssignments/read", - "Microsoft.Advisor/generateRecommendations/action", - "Microsoft.Advisor/register/action", - "Microsoft.Advisor/unregister/action", - "Microsoft.Advisor/configurations/read", - "Microsoft.Advisor/configurations/write", - "Microsoft.Advisor/generateRecommendations/read", - "Microsoft.Advisor/operations/read", - "Microsoft.Advisor/recommendations/read", - "Microsoft.Advisor/recommendations/available/action", - "Microsoft.Advisor/recommendations/suppressions/read", - "Microsoft.Advisor/recommendations/suppressions/write", - "Microsoft.Advisor/recommendations/suppressions/delete", - "Microsoft.Capacity/catalogs/read", - "Microsoft.Capacity/register/action", - "Microsoft.Compute/register/action", - "Microsoft.Compute/capacityReservationGroups/read", - "Microsoft.Compute/operations/read", - "Microsoft.Compute/availabilitySets/vmSizes/read", - "Microsoft.Compute/availabilitySets/read", - "Microsoft.Compute/capacityReservationGroups/capacityReservations/read", - "Microsoft.Compute/locations/capsOperations/read", - "Microsoft.Compute/cloudServices/instanceView/read", - "Microsoft.Compute/cloudServices/providers/Microsoft.Insights/metricDefinitions/read", - "Microsoft.Compute/cloudServices/roles/providers/Microsoft.Insights/metricDefinitions/read", - "Microsoft.Compute/locations/publishers/artifacttypes/offers/skus/read", - "Microsoft.Compute/skus/read", - "Microsoft.Compute/locations/usages/read", - "Microsoft.Compute/virtualMachineScaleSets/vmSizes/read", - "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/read", - "Microsoft.Compute/locations/vmSizes/read", - "Microsoft.Compute/virtualMachines/read", - "Microsoft.Compute/virtualMachines/vmSizes/read", - "Microsoft.Consumption/register/action", - "Microsoft.Consumption/reservationRecommendations/read", - "Microsoft.CostManagement/query/action", - "Microsoft.CostManagement/reports/action", - "Microsoft.CostManagement/exports/action", - "Microsoft.CostManagement/register/action", - "Microsoft.CostManagement/views/action", - "Microsoft.CostManagement/forecast/action", - "Microsoft.CostManagement/alerts/read", - "Microsoft.CostManagement/cloudConnectors/read", - "Microsoft.CostManagement/dimensions/read", - "Microsoft.CostManagement/exports/read", - "Microsoft.CostManagement/exports/write", - "Microsoft.CostManagement/exports/delete", - "Microsoft.CostManagement/exports/run/action", - "Microsoft.CostManagement/externalBillingAccounts/read", - "Microsoft.CostManagement/externalBillingAccounts/query/action", - "Microsoft.CostManagement/externalBillingAccounts/forecast/action", - "Microsoft.CostManagement/externalBillingAccounts/dimensions/read", - "Microsoft.CostManagement/externalBillingAccounts/query/read", - "Microsoft.CostManagement/externalBillingAccounts/externalSubscriptions/read", - "Microsoft.CostManagement/externalBillingAccounts/forecast/read", - "Microsoft.CostManagement/externalSubscriptions/read", - "Microsoft.CostManagement/externalSubscriptions/query/action", - "Microsoft.CostManagement/externalSubscriptions/forecast/action", - "Microsoft.CostManagement/externalSubscriptions/dimensions/read", - "Microsoft.CostManagement/externalSubscriptions/query/read", - "Microsoft.CostManagement/externalSubscriptions/forecast/read", - "Microsoft.CostManagement/forecast/read", - "Microsoft.CostManagement/operations/read", - "Microsoft.CostManagement/query/read", - "Microsoft.CostManagement/reports/read", - "Microsoft.CostManagement/views/read", - "Microsoft.CostManagement/views/delete", - "Microsoft.CostManagement/views/write", - "Microsoft.CostManagement/tenants/register/action", - "Microsoft.CostManagement/budgets/read", - "Microsoft.Insights/MetricDefinitions/Read", - "Microsoft.Insights/Metrics/Read", - "Microsoft.Resources/tags/read", - "Microsoft.Resources/subscriptions/read", - "Microsoft.Resources/subscriptions/resourceGroups/read", - "Microsoft.Support/supportTickets/read", - "Microsoft.Support/supportTickets/write", - "Microsoft.SQL/register/action" - ], - "notActions": [], - "dataActions": [], - "notDataActions": [] - } - ] - } -} \ No newline at end of file diff --git a/Policies/Azure/Spot-Azure-ECO-ReadOnly.json b/Policies/Azure/Spot-Azure-ECO-ReadOnly.json deleted file mode 100644 index 215ff70..0000000 --- a/Policies/Azure/Spot-Azure-ECO-ReadOnly.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "properties": { - "roleName": "Spot Eco", - "description": "Spot Eco read-only role to be granted to Spot Eco users to each subscription for which an assessment is desired", - "assignableScopes": ["/subscriptions/"], - "permissions": [ - { - "actions": [ - "Microsoft.Authorization/roleAssignments/read", - "Microsoft.Advisor/advisorScore/read", - - "Microsoft.Capacity/catalogs/read", - "Microsoft.Capacity/register/action", - "Microsoft.Compute/register/action", - "Microsoft.Compute/capacityReservationGroups/read", - "Microsoft.Compute/operations/read", - "Microsoft.Compute/availabilitySets/vmSizes/read", - "Microsoft.Compute/availabilitySets/read", - "Microsoft.Compute/capacityReservationGroups/capacityReservations/read", - "Microsoft.Compute/locations/capsOperations/read", - "Microsoft.Compute/cloudServices/instanceView/read", - "Microsoft.Compute/cloudServices/providers/Microsoft.Insights/metricDefinitions/read", - "Microsoft.Compute/cloudServices/roles/providers/Microsoft.Insights/metricDefinitions/read", - "Microsoft.Compute/locations/publishers/artifacttypes/offers/skus/read", - "Microsoft.Compute/skus/read", - "Microsoft.Compute/locations/usages/read", - "Microsoft.Compute/virtualMachineScaleSets/vmSizes/read", - "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/read", - "Microsoft.Compute/locations/vmSizes/read", - "Microsoft.Compute/virtualMachines/read", - "Microsoft.Compute/virtualMachines/vmSizes/read", - "Microsoft.Consumption/register/action", - "Microsoft.Consumption/reservationRecommendations/read", - "Microsoft.CostManagement/query/action", - "Microsoft.CostManagement/reports/action", - "Microsoft.CostManagement/exports/action", - "Microsoft.CostManagement/register/action", - "Microsoft.CostManagement/views/action", - "Microsoft.CostManagement/forecast/action", - "Microsoft.CostManagement/alerts/read", - "Microsoft.CostManagement/cloudConnectors/read", - "Microsoft.CostManagement/dimensions/read", - "Microsoft.CostManagement/exports/read", - "Microsoft.CostManagement/exports/write", - "Microsoft.CostManagement/exports/delete", - "Microsoft.CostManagement/exports/run/action", - "Microsoft.CostManagement/externalBillingAccounts/read", - "Microsoft.CostManagement/externalBillingAccounts/query/action", - "Microsoft.CostManagement/externalBillingAccounts/forecast/action", - "Microsoft.CostManagement/externalBillingAccounts/dimensions/read", - "Microsoft.CostManagement/externalBillingAccounts/query/read", - "Microsoft.CostManagement/externalBillingAccounts/externalSubscriptions/read", - "Microsoft.CostManagement/externalBillingAccounts/forecast/read", - "Microsoft.CostManagement/externalSubscriptions/read", - "Microsoft.CostManagement/externalSubscriptions/query/action", - "Microsoft.CostManagement/externalSubscriptions/forecast/action", - "Microsoft.CostManagement/externalSubscriptions/dimensions/read", - "Microsoft.CostManagement/externalSubscriptions/query/read", - "Microsoft.CostManagement/externalSubscriptions/forecast/read", - "Microsoft.CostManagement/forecast/read", - "Microsoft.CostManagement/operations/read", - "Microsoft.CostManagement/query/read", - "Microsoft.CostManagement/reports/read", - "Microsoft.CostManagement/views/read", - "Microsoft.CostManagement/views/delete", - "Microsoft.CostManagement/views/write", - "Microsoft.CostManagement/tenants/register/action", - "Microsoft.CostManagement/budgets/read", - "Microsoft.Insights/MetricDefinitions/Read", - "Microsoft.Insights/Metrics/Read", - "Microsoft.Resources/tags/read", - "Microsoft.Resources/subscriptions/read", - "Microsoft.Resources/subscriptions/resourceGroups/read", - "Microsoft.SQL/register/action" - ], - "notActions": [], - "dataActions": [], - "notDataActions": [] - } - ] - } -} \ No newline at end of file diff --git a/Utilities/AzureSetup/CSP/eco_azure_full_access.ps1 b/Utilities/AzureSetup/CSP/eco_azure_full_access.ps1 new file mode 100644 index 0000000..4577210 --- /dev/null +++ b/Utilities/AzureSetup/CSP/eco_azure_full_access.ps1 @@ -0,0 +1,51 @@ +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" +$billingAccountId = "{{billing_account_id}}" + +# Connect to Azure AD +Connect-AzAccount -TenantId $tenantId + +# Register the app +$app = New-AzADApplication -DisplayName $appName + +# Create a service principal +New-AzADServicePrincipal -ApplicationId $app.AppId + +# Delete all secret keys +$secretKeys = Get-AzADAppCredential -ApplicationId $appId +foreach ($secretKey in $secretKeys) { + # Remove the secret key + Remove-AzADAppCredential -ApplicationId $appId -KeyId $secretKey.KeyId +} + +# Create a client secret +$secret = New-AzADAppCredential -ApplicationId $app.AppId -StartDate (Get-Date) -EndDate (Get-Date).AddYears(1) + +# Role assignments +# assign reservation reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Reader" -Scope "/providers/Microsoft.Capacity" + +# assign reservation purchaser role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Purchaser" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign reservation administrator role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Administrator" -Scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Reader" -Scope "/providers/Microsoft.BillingBenefits" + +# assign savings plan purchaser role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Purchaser" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign savings plan administrator role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Administrator" -Scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign billing reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Billing Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" + +# Output app details +Write-Host "App ID:" $app.AppId +Write-Host "Secret Key:" $secret.SecretText diff --git a/Utilities/AzureSetup/CSP/eco_azure_full_access.py b/Utilities/AzureSetup/CSP/eco_azure_full_access.py new file mode 100644 index 0000000..8c3e202 --- /dev/null +++ b/Utilities/AzureSetup/CSP/eco_azure_full_access.py @@ -0,0 +1,132 @@ +import requests +from azure.identity import DefaultAzureCredential + +# Set up the necessary variables +TENANT_ID = "{{tenant_id}}" +APP_NAME = "{{app_name}}" +BILLING_ACCOUNT_ID = "{{billing_account_id}}" + +# Authenticate using DefaultAzureCredential +credential = DefaultAzureCredential() +graph_token = credential.get_token("https://graph.microsoft.com/.default") +management_token = credential.get_token("https://management.azure.com/.default") +graph_header = {"Authorization": f"Bearer {graph_token.token}", "Content-Type": "application/json"} +management_header = {"Authorization": f"Bearer {management_token.token}", "Content-Type": "application/json"} + +# Register the application +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications", json={"displayName": APP_NAME}) +app_id = resp.json()["appId"] + +# Remove the existing secret keys +resp = requests.get(f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')", headers=graph_header) +resp.raise_for_status() +keys = [pc["keyId"] for pc in resp.json()["passwordCredentials"]] +for key in keys: + delete_response = requests.post(url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/removePassword", headers=graph_header, json={"keyId": key}) + delete_response.raise_for_status() + +# Create the secret key +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/addPassword", json={"passwordCredential": {}}) +resp.raise_for_status() +secret_key = resp.json()["secretText"] + +# Create the service principal +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/servicePrincipals", json={"appId": app_id}) +resp.raise_for_status() +object_id = resp.json()["id"] + +# setup for role assignments +# assign reservation reader role +role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign reservation purchaser role +role_definition_id = "f7b75c60-3036-4b75-91c3-6b41c27c1689" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign reservation administrator role +role_definition_id = "a8889054-8d42-49c9-bc1c-52486c10e7cd" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign savings plan purchaser role +role_definition_id = "3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign savings plan administrator role +role_definition_id = "433febaf-a31d-4d4f-8dc8-b4593b39bda5" +scope = "providers/Microsoft.BillingBenefits" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign cost management reader role +role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign billing reader role +role_definition_id = "50000000-aaaa-bbbb-cccc-100000000002" +scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + + +print(f"App ID: {app_id}") +print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/CSP/eco_azure_full_access.sh b/Utilities/AzureSetup/CSP/eco_azure_full_access.sh new file mode 100644 index 0000000..b883de3 --- /dev/null +++ b/Utilities/AzureSetup/CSP/eco_azure_full_access.sh @@ -0,0 +1,46 @@ +APP_NAME="{{app_name}}" +TENANT_ID="{{tenant_id}}" +BILLING_ACCOUNT_ID="{{billing_account_id}}" + +az login --tenant $TENANT_ID + +# Create app +APP_ID=$(az ad app create --display-name $APP_NAME --output json --query appId) +APP_ID=$(echo $APP_ID | tr -d '"') + +# Generate secret key +SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query password) +SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') + +# Create service principal +az ad sp create --id $APP_ID + +# Role assignments +# assign reservation reader role +az role assignment create --assignee $APP_ID --role "Reservations Reader" --scope "/providers/Microsoft.Capacity" + +# assign reservation purchaser role +az role assignment create --assignee $APP_ID --role "Reservations Purchaser" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign reservation administrator role +az role assignment create --assignee $APP_ID --role "Reservations Administrator" --scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits" + +# assign savings plan purchaser role +az role assignment create --assignee $APP_ID --role "Savings plan Purchaser" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign savings plan administrator role +az role assignment create --assignee $APP_ID --role "Savings plan Administrator" --scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign billing reader role +az role assignment create --assignee $APP_ID --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" + + +# Print registered app info +echo "App ID: $APP_ID" +echo "Secret Key: $SECRET_KEY" diff --git a/Utilities/AzureSetup/CSP/eco_azure_full_access.tf b/Utilities/AzureSetup/CSP/eco_azure_full_access.tf new file mode 100644 index 0000000..a1fcddb --- /dev/null +++ b/Utilities/AzureSetup/CSP/eco_azure_full_access.tf @@ -0,0 +1,103 @@ +variable "tenant_id" { + description = "The Tenant ID for the Azure subscription" + type = string + default = "" +} + +variable "billing_account_id" { + description = "The Billing Account ID" + type = string + default = "" +} + +# Provider block +provider "azuread" { + version = "3.0.0" +} +provider "azurerm" { + version = "3.0.0" + features {} +} +# Resource block +# Create an Azure AD application registration +resource "azuread_application_registration" "create_registered_app" { + display_name = "EcoAzureADApp3" +} +# Create an Azure AD service principal +resource "azuread_service_principal" "create_service_principal" { + client_id = azuread_application_registration.create_registered_app.client_id +} +# Create an Azure AD application password +resource "azuread_application_password" "create_secret_key" { + application_id = azuread_application_registration.create_registered_app.id +} + +# Role assignments +# assign reservation reader role +resource "null_resource" "reservation_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Reader" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign reservation purchaser role +resource "azurerm_role_assignment" "reservation_purchaser_role_assignment" { + role_definition_name = "Reservations Purchaser" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign reservation administrator role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Administrator" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign savings plan purchaser role +resource "azurerm_role_assignment" "reservation_purchaser_role_assignment" { + role_definition_name = "Savings plan Purchaser" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign savings plan reader role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign savings plan administrator role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Administrator" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign cost management reader role +resource "azurerm_role_assignment" "cost_management_reader_role_assignment" { + role_definition_name = "Cost Management Reader" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign billing reader role +resource "null_resource" "billing_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${var.billing_account_id}"' + } +} + +# Output +output "client_id" { + value = azuread_application_registration.create_registered_app.client_id +} +output "object_id" { + value = azuread_service_principal.create_service_principal.object_id +} +output "application_secret" { + value = azuread_application_password.create_secret_key.value + sensitive = true +} +data "azuread_client_config" "current" {} diff --git a/Utilities/AzureSetup/CSP/eco_azure_readonly.ps1 b/Utilities/AzureSetup/CSP/eco_azure_readonly.ps1 new file mode 100644 index 0000000..c347182 --- /dev/null +++ b/Utilities/AzureSetup/CSP/eco_azure_readonly.ps1 @@ -0,0 +1,40 @@ +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" +$billingAccountId = "{{billing_account_id}}" + +# Connect to Azure AD +Connect-AzAccount -TenantId $tenantId + +# Register the app +$app = New-AzADApplication -DisplayName $appName + +# Create a service principal +New-AzADServicePrincipal -ApplicationId $app.AppId + +# Delete all secret keys +$secretKeys = Get-AzADAppCredential -ApplicationId $appId +foreach ($secretKey in $secretKeys) { + # Remove the secret key + Remove-AzADAppCredential -ApplicationId $appId -KeyId $secretKey.KeyId +} + +# Create a client secret +$secret = New-AzADAppCredential -ApplicationId $app.AppId -StartDate (Get-Date) -EndDate (Get-Date).AddYears(1) + +# Role assignments +# assign reservation reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Reader" -Scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Reader" -Scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign billing reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Billing Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId " + + +# Output app details +Write-Host "App ID:" $app.AppId +Write-Host "Secret Key:" $secret.SecretText diff --git a/Utilities/AzureSetup/CSP/eco_azure_readonly.py b/Utilities/AzureSetup/CSP/eco_azure_readonly.py new file mode 100644 index 0000000..ed15958 --- /dev/null +++ b/Utilities/AzureSetup/CSP/eco_azure_readonly.py @@ -0,0 +1,82 @@ +import requests +from azure.identity import DefaultAzureCredential + +from Utilities.AzureSetup.CSP.eco_azure_full_access import BILLING_ACCOUNT_ID + +# Set up the necessary variables +TENANT_ID = "{{tenant_id}}" +APP_NAME = "{{app_name}}" +BILLING_ACCOUNT_ID = "{{billing_account_id}}" + +# Authenticate using DefaultAzureCredential +credential = DefaultAzureCredential() +graph_token = credential.get_token("https://graph.microsoft.com/.default") +management_token = credential.get_token("https://management.azure.com/.default") +graph_header = {"Authorization": f"Bearer {graph_token.token}", "Content-Type": "application/json"} +management_header = {"Authorization": f"Bearer {management_token.token}", "Content-Type": "application/json"} + +# Register the application +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications", json={"displayName": APP_NAME}) +app_id = resp.json()["appId"] + +# Remove the existing secret keys +resp = requests.get(f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')", headers=graph_header) +resp.raise_for_status() +keys = [pc["keyId"] for pc in resp.json()["passwordCredentials"]] +for key in keys: + delete_response = requests.post(url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/removePassword", headers=graph_header, json={"keyId": key}) + delete_response.raise_for_status() + +# Create the secret key +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/addPassword", json={"passwordCredential": {}}) +resp.raise_for_status() +secret_key = resp.json()["secretText"] + +# Create the service principal +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/servicePrincipals", json={"appId": app_id}) +resp.raise_for_status() +object_id = resp.json()["id"] + +# setup for role assignments +# assign reservation reader role +role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign cost management reader role +role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign billing reader role +role_definition_id = "50000000-aaaa-bbbb-cccc-100000000002" +scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + + +print(f"App ID: {app_id}") +print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/CSP/eco_azure_readonly.sh b/Utilities/AzureSetup/CSP/eco_azure_readonly.sh new file mode 100644 index 0000000..f5ed213 --- /dev/null +++ b/Utilities/AzureSetup/CSP/eco_azure_readonly.sh @@ -0,0 +1,34 @@ +APP_NAME="{{app_name}}" +TENANT_ID="{{tenant_id}}" +BILLING_ACCOUNT_ID="{{billing_account_id}}" + +az login --tenant $TENANT_ID + +# Create app +APP_ID=$(az ad app create --display-name $APP_NAME --output json --query appId) +APP_ID=$(echo $APP_ID | tr -d '"') + +# Generate secret key +SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query password) +SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') + +# Create service principal +az ad sp create --id $APP_ID + +# Role assignments +# assign reservation reader role +az role assignment create --assignee $APP_ID --role "Reservations Reader" --scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign billing reader role +az role assignment create --assignee $APP_ID --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" + + +# Print registered app info +echo "App ID: $APP_ID" +echo "Secret Key: $SECRET_KEY" diff --git a/Utilities/AzureSetup/CSP/eco_azure_readonly.tf b/Utilities/AzureSetup/CSP/eco_azure_readonly.tf new file mode 100644 index 0000000..c6c5755 --- /dev/null +++ b/Utilities/AzureSetup/CSP/eco_azure_readonly.tf @@ -0,0 +1,75 @@ +variable "tenant_id" { + description = "The Tenant ID for the Azure subscription" + type = string + default = "" +} + +variable "billing_account_id" { + description = "The Billing Account ID" + type = string + default = "" +} + +# Provider block +provider "azuread" { + version = "3.0.0" +} +provider "azurerm" { + version = "3.0.0" + features {} +} +# Resource block +# Create an Azure AD application registration +resource "azuread_application_registration" "create_registered_app" { + display_name = "EcoAzureADApp3" +} +# Create an Azure AD service principal +resource "azuread_service_principal" "create_service_principal" { + client_id = azuread_application_registration.create_registered_app.client_id +} +# Create an Azure AD application password +resource "azuread_application_password" "create_secret_key" { + application_id = azuread_application_registration.create_registered_app.id +} + +# Role assignments +# assign reservation reader role +resource "null_resource" "reservation_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Reader" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign savings plan reader role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign cost management reader role +resource "azurerm_role_assignment" "cost_management_reader_role_assignment" { + role_definition_name = "Cost Management Reader" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign billing reader role +resource "null_resource" "billing_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${var.billing_account_id}"' + } +} + +# Output +output "client_id" { + value = azuread_application_registration.create_registered_app.client_id +} +output "object_id" { + value = azuread_service_principal.create_service_principal.object_id +} +output "application_secret" { + value = azuread_application_password.create_secret_key.value + sensitive = true +} +data "azuread_client_config" "current" {} diff --git a/Utilities/AzureSetup/EA/eco_azure_full_access.ps1 b/Utilities/AzureSetup/EA/eco_azure_full_access.ps1 new file mode 100644 index 0000000..c6a11bd --- /dev/null +++ b/Utilities/AzureSetup/EA/eco_azure_full_access.ps1 @@ -0,0 +1,52 @@ +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" +$billingAccountId = "{{billing_account_id}}" + +# Connect to Azure AD +Connect-AzAccount -TenantId $tenantId + +# Register the app +$app = New-AzADApplication -DisplayName $appName + +# Create a service principal +New-AzADServicePrincipal -ApplicationId $app.AppId + +# Delete all secret keys +$secretKeys = Get-AzADAppCredential -ApplicationId $appId +foreach ($secretKey in $secretKeys) { + # Remove the secret key + Remove-AzADAppCredential -ApplicationId $appId -KeyId $secretKey.KeyId +} + +# Create a client secret +$secret = New-AzADAppCredential -ApplicationId $app.AppId -StartDate (Get-Date) -EndDate (Get-Date).AddYears(1) + +# Role assignments +# assign reservation reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Reader" -Scope "/providers/Microsoft.Capacity" + +# assign reservation purchaser role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Purchaser" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign reservation administrator role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Administrator" -Scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Reader" -Scope "/providers/Microsoft.BillingBenefits" + +# assign savings plan purchaser role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Purchaser" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign savings plan administrator role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Administrator" -Scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign enrollment reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Enrollment Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" + + +# Output app details +Write-Host "App ID:" $app.AppId +Write-Host "Secret Key:" $secret.SecretText diff --git a/Utilities/AzureSetup/EA/eco_azure_full_access.py b/Utilities/AzureSetup/EA/eco_azure_full_access.py new file mode 100644 index 0000000..cc13584 --- /dev/null +++ b/Utilities/AzureSetup/EA/eco_azure_full_access.py @@ -0,0 +1,132 @@ +import requests +from azure.identity import DefaultAzureCredential + +# Set up the necessary variables +TENANT_ID = "{{tenant_id}}" +APP_NAME = "{{app_name}}" +BILLING_ACCOUNT_ID = "{{billing_account_id}}" + +# Authenticate using DefaultAzureCredential +credential = DefaultAzureCredential() +graph_token = credential.get_token("https://graph.microsoft.com/.default") +management_token = credential.get_token("https://management.azure.com/.default") +graph_header = {"Authorization": f"Bearer {graph_token.token}", "Content-Type": "application/json"} +management_header = {"Authorization": f"Bearer {management_token.token}", "Content-Type": "application/json"} + +# Register the application +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications", json={"displayName": APP_NAME}) +app_id = resp.json()["appId"] + +# Remove the existing secret keys +resp = requests.get(f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')", headers=graph_header) +resp.raise_for_status() +keys = [pc["keyId"] for pc in resp.json()["passwordCredentials"]] +for key in keys: + delete_response = requests.post(url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/removePassword", headers=graph_header, json={"keyId": key}) + delete_response.raise_for_status() + +# Create the secret key +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/addPassword", json={"passwordCredential": {}}) +resp.raise_for_status() +secret_key = resp.json()["secretText"] + +# Create the service principal +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/servicePrincipals", json={"appId": app_id}) +resp.raise_for_status() +object_id = resp.json()["id"] + +# setup for role assignments +# assign reservation reader role +role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign reservation purchaser role +role_definition_id = "f7b75c60-3036-4b75-91c3-6b41c27c1689" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign reservation administrator role +role_definition_id = "a8889054-8d42-49c9-bc1c-52486c10e7cd" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign savings plan purchaser role +role_definition_id = "3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign savings plan administrator role +role_definition_id = "433febaf-a31d-4d4f-8dc8-b4593b39bda5" +scope = "providers/Microsoft.BillingBenefits" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign cost management reader role +role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign enrollment reader role +role_definition_id = "24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" +scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + + +print(f"App ID: {app_id}") +print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/EA/eco_azure_full_access.sh b/Utilities/AzureSetup/EA/eco_azure_full_access.sh new file mode 100644 index 0000000..f03f8f7 --- /dev/null +++ b/Utilities/AzureSetup/EA/eco_azure_full_access.sh @@ -0,0 +1,47 @@ +APP_NAME="{{app_name}}" +TENANT_ID="{{tenant_id}}" +BILLING_ACCOUNT_ID="{{billing_account_id}}" + + +az login --tenant $TENANT_ID + +# Create app +APP_ID=$(az ad app create --display-name $APP_NAME --output json --query appId) +APP_ID=$(echo $APP_ID | tr -d '"') + +# Generate secret key +SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query password) +SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') + +# Create service principal +az ad sp create --id $APP_ID + +# Role assignments +# assign reservation reader role +az role assignment create --assignee $APP_ID --role "Reservations Reader" --scope "/providers/Microsoft.Capacity" + +# assign reservation purchaser role +az role assignment create --assignee $APP_ID --role "Reservations Purchaser" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign reservation administrator role +az role assignment create --assignee $APP_ID --role "Reservations Administrator" --scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits" + +# assign savings plan purchaser role +az role assignment create --assignee $APP_ID --role "Savings plan Purchaser" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign savings plan administrator role +az role assignment create --assignee $APP_ID --role "Savings plan Administrator" --scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign enrollment reader role +az role assignment create --assignee $APP_ID --role "Enrollment Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" + + +# Print registered app info +echo "App ID: $APP_ID" +echo "Secret Key: $SECRET_KEY" diff --git a/Utilities/AzureSetup/EA/eco_azure_full_access.tf b/Utilities/AzureSetup/EA/eco_azure_full_access.tf new file mode 100644 index 0000000..96aab5a --- /dev/null +++ b/Utilities/AzureSetup/EA/eco_azure_full_access.tf @@ -0,0 +1,103 @@ +variable "tenant_id" { + description = "The Tenant ID for the Azure subscription" + type = string + default = "" +} + +variable "billing_account_id" { + description = "The Billing Account ID" + type = string + default = "" +} + +# Provider block +provider "azuread" { + version = "3.0.0" +} +provider "azurerm" { + version = "3.0.0" + features {} +} +# Resource block +# Create an Azure AD application registration +resource "azuread_application_registration" "create_registered_app" { + display_name = "EcoAzureADApp3" +} +# Create an Azure AD service principal +resource "azuread_service_principal" "create_service_principal" { + client_id = azuread_application_registration.create_registered_app.client_id +} +# Create an Azure AD application password +resource "azuread_application_password" "create_secret_key" { + application_id = azuread_application_registration.create_registered_app.id +} + +# Role assignments +# assign reservation reader role +resource "null_resource" "reservation_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Reader" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign reservation purchaser role +resource "azurerm_role_assignment" "reservation_purchaser_role_assignment" { + role_definition_name = "Reservations Purchaser" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign reservation administrator role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Administrator" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign savings plan reader role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign savings plan purchaser role +resource "azurerm_role_assignment" "reservation_purchaser_role_assignment" { + role_definition_name = "Savings plan Purchaser" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign savings plan administrator role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Administrator" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign cost management reader role +resource "azurerm_role_assignment" "cost_management_reader_role_assignment" { + role_definition_name = "Cost Management Reader" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign enrollment reader role +resource "null_resource" "enrollment_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Enrollment Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${var.billing_account_id}"' + } +} + +# Output +output "client_id" { + value = azuread_application_registration.create_registered_app.client_id +} +output "object_id" { + value = azuread_service_principal.create_service_principal.object_id +} +output "application_secret" { + value = azuread_application_password.create_secret_key.value + sensitive = true +} +data "azuread_client_config" "current" {} diff --git a/Utilities/AzureSetup/EA/eco_azure_readonly.ps1 b/Utilities/AzureSetup/EA/eco_azure_readonly.ps1 new file mode 100644 index 0000000..a0e8f78 --- /dev/null +++ b/Utilities/AzureSetup/EA/eco_azure_readonly.ps1 @@ -0,0 +1,40 @@ +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" +$billingAccountId = "{{billing_account_id}}" + +# Connect to Azure AD +Connect-AzAccount -TenantId $tenantId + +# Register the app +$app = New-AzADApplication -DisplayName $appName + +# Create a service principal +New-AzADServicePrincipal -ApplicationId $app.AppId + +# Delete all secret keys +$secretKeys = Get-AzADAppCredential -ApplicationId $appId +foreach ($secretKey in $secretKeys) { + # Remove the secret key + Remove-AzADAppCredential -ApplicationId $appId -KeyId $secretKey.KeyId +} + +# Create a client secret +$secret = New-AzADAppCredential -ApplicationId $app.AppId -StartDate (Get-Date) -EndDate (Get-Date).AddYears(1) + +# Role assignments +# assign reservation reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Reader" -Scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Reader" -Scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign enrollment reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Enrollment Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" + + +# Output app details +Write-Host "App ID:" $app.AppId +Write-Host "Secret Key:" $secret.SecretText diff --git a/Utilities/AzureSetup/EA/eco_azure_readonly.py b/Utilities/AzureSetup/EA/eco_azure_readonly.py new file mode 100644 index 0000000..3de574f --- /dev/null +++ b/Utilities/AzureSetup/EA/eco_azure_readonly.py @@ -0,0 +1,80 @@ +import requests +from azure.identity import DefaultAzureCredential + +# Set up the necessary variables +TENANT_ID = "{{tenant_id}}" +APP_NAME = "{{app_name}}" +BILLING_ACCOUNT_ID = "{{billing_account_id}}" + +# Authenticate using DefaultAzureCredential +credential = DefaultAzureCredential() +graph_token = credential.get_token("https://graph.microsoft.com/.default") +management_token = credential.get_token("https://management.azure.com/.default") +graph_header = {"Authorization": f"Bearer {graph_token.token}", "Content-Type": "application/json"} +management_header = {"Authorization": f"Bearer {management_token.token}", "Content-Type": "application/json"} + +# Register the application +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications", json={"displayName": APP_NAME}) +app_id = resp.json()["appId"] + +# Remove the existing secret keys +resp = requests.get(f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')", headers=graph_header) +resp.raise_for_status() +keys = [pc["keyId"] for pc in resp.json()["passwordCredentials"]] +for key in keys: + delete_response = requests.post(url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/removePassword", headers=graph_header, json={"keyId": key}) + delete_response.raise_for_status() + +# Create the secret key +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/addPassword", json={"passwordCredential": {}}) +resp.raise_for_status() +secret_key = resp.json()["secretText"] + +# Create the service principal +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/servicePrincipals", json={"appId": app_id}) +resp.raise_for_status() +object_id = resp.json()["id"] + +# setup for role assignments +# assign reservation reader role +role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign cost management reader role +role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign enrollment reader role +role_definition_id = "24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" +scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + + +print(f"App ID: {app_id}") +print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/EA/eco_azure_readonly.sh b/Utilities/AzureSetup/EA/eco_azure_readonly.sh new file mode 100644 index 0000000..920602c --- /dev/null +++ b/Utilities/AzureSetup/EA/eco_azure_readonly.sh @@ -0,0 +1,34 @@ +APP_NAME="{{app_name}}" +TENANT_ID="{{tenant_id}}" +BILLING_ACCOUNT_ID="{{billing_account_id}}" + +az login --tenant $TENANT_ID + +# Create app +APP_ID=$(az ad app create --display-name $APP_NAME --output json --query appId) +APP_ID=$(echo $APP_ID | tr -d '"') + +# Generate secret key +SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query password) +SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') + +# Create service principal +az ad sp create --id $APP_ID + +# Role assignments +# assign reservation reader role +az role assignment create --assignee "{{app_id}}" --role "Reservations Reader" --scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +az role assignment create --assignee "{{app_id}}" --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign enrollment reader role +az role assignment create --assignee "{{app_id}}" --role "Enrollment Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" + + +# Print registered app info +echo "App ID: $APP_ID" +echo "Secret Key: $SECRET_KEY" diff --git a/Utilities/AzureSetup/EA/eco_azure_readonly.tf b/Utilities/AzureSetup/EA/eco_azure_readonly.tf new file mode 100644 index 0000000..12b8c75 --- /dev/null +++ b/Utilities/AzureSetup/EA/eco_azure_readonly.tf @@ -0,0 +1,75 @@ +variable "tenant_id" { + description = "The Tenant ID for the Azure subscription" + type = string + default = "" +} + +variable "billing_account_id" { + description = "The Billing Account ID" + type = string + default = "" +} + +# Provider block +provider "azuread" { + version = "3.0.0" +} +provider "azurerm" { + version = "3.0.0" + features {} +} +# Resource block +# Create an Azure AD application registration +resource "azuread_application_registration" "create_registered_app" { + display_name = "EcoAzureADApp3" +} +# Create an Azure AD service principal +resource "azuread_service_principal" "create_service_principal" { + client_id = azuread_application_registration.create_registered_app.client_id +} +# Create an Azure AD application password +resource "azuread_application_password" "create_secret_key" { + application_id = azuread_application_registration.create_registered_app.id +} + +# Role assignments +# assign reservation reader role +resource "null_resource" "reservation_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Reader" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign savings plan reader role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign cost management reader role +resource "azurerm_role_assignment" "cost_management_reader_role_assignment" { + role_definition_name = "Cost Management Reader" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign enrollment reader role +resource "null_resource" "enrollment_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Enrollment Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${var.billing_account_id}"' + } +} + +# Output +output "client_id" { + value = azuread_application_registration.create_registered_app.client_id +} +output "object_id" { + value = azuread_service_principal.create_service_principal.object_id +} +output "application_secret" { + value = azuread_application_password.create_secret_key.value + sensitive = true +} +data "azuread_client_config" "current" {} diff --git a/Utilities/AzureSetup/MCA/eco_azure_full_access.ps1 b/Utilities/AzureSetup/MCA/eco_azure_full_access.ps1 new file mode 100644 index 0000000..ffd6e1d --- /dev/null +++ b/Utilities/AzureSetup/MCA/eco_azure_full_access.ps1 @@ -0,0 +1,52 @@ +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" +$billingAccountId = "{{billing_account_id}}" + +# Connect to Azure AD +Connect-AzAccount -TenantId $tenantId + +# Register the app +$app = New-AzADApplication -DisplayName $appName + +# Create a service principal +New-AzADServicePrincipal -ApplicationId $app.AppId + +# Delete all secret keys +$secretKeys = Get-AzADAppCredential -ApplicationId $appId +foreach ($secretKey in $secretKeys) { + # Remove the secret key + Remove-AzADAppCredential -ApplicationId $appId -KeyId $secretKey.KeyId +} + +# Create a client secret +$secret = New-AzADAppCredential -ApplicationId $app.AppId -StartDate (Get-Date) -EndDate (Get-Date).AddYears(1) + +# Role assignments +# assign reservation reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Reader" -Scope "/providers/Microsoft.Capacity" + +# assign reservation purchaser role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Purchaser" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign reservation administrator role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Administrator" -Scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Reader" -Scope "/providers/Microsoft.BillingBenefits" + +# assign savings plan purchaser role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Purchaser" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign savings plan administrator role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Administrator" -Scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign billing reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Billing Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" + + +# Output app details +Write-Host "App ID:" $app.AppId +Write-Host "Secret Key:" $secret.SecretText diff --git a/Utilities/AzureSetup/MCA/eco_azure_full_access.py b/Utilities/AzureSetup/MCA/eco_azure_full_access.py new file mode 100644 index 0000000..8c3e202 --- /dev/null +++ b/Utilities/AzureSetup/MCA/eco_azure_full_access.py @@ -0,0 +1,132 @@ +import requests +from azure.identity import DefaultAzureCredential + +# Set up the necessary variables +TENANT_ID = "{{tenant_id}}" +APP_NAME = "{{app_name}}" +BILLING_ACCOUNT_ID = "{{billing_account_id}}" + +# Authenticate using DefaultAzureCredential +credential = DefaultAzureCredential() +graph_token = credential.get_token("https://graph.microsoft.com/.default") +management_token = credential.get_token("https://management.azure.com/.default") +graph_header = {"Authorization": f"Bearer {graph_token.token}", "Content-Type": "application/json"} +management_header = {"Authorization": f"Bearer {management_token.token}", "Content-Type": "application/json"} + +# Register the application +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications", json={"displayName": APP_NAME}) +app_id = resp.json()["appId"] + +# Remove the existing secret keys +resp = requests.get(f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')", headers=graph_header) +resp.raise_for_status() +keys = [pc["keyId"] for pc in resp.json()["passwordCredentials"]] +for key in keys: + delete_response = requests.post(url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/removePassword", headers=graph_header, json={"keyId": key}) + delete_response.raise_for_status() + +# Create the secret key +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/addPassword", json={"passwordCredential": {}}) +resp.raise_for_status() +secret_key = resp.json()["secretText"] + +# Create the service principal +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/servicePrincipals", json={"appId": app_id}) +resp.raise_for_status() +object_id = resp.json()["id"] + +# setup for role assignments +# assign reservation reader role +role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign reservation purchaser role +role_definition_id = "f7b75c60-3036-4b75-91c3-6b41c27c1689" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign reservation administrator role +role_definition_id = "a8889054-8d42-49c9-bc1c-52486c10e7cd" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign savings plan purchaser role +role_definition_id = "3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign savings plan administrator role +role_definition_id = "433febaf-a31d-4d4f-8dc8-b4593b39bda5" +scope = "providers/Microsoft.BillingBenefits" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign cost management reader role +role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign billing reader role +role_definition_id = "50000000-aaaa-bbbb-cccc-100000000002" +scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + + +print(f"App ID: {app_id}") +print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/MCA/eco_azure_full_access.sh b/Utilities/AzureSetup/MCA/eco_azure_full_access.sh new file mode 100644 index 0000000..b883de3 --- /dev/null +++ b/Utilities/AzureSetup/MCA/eco_azure_full_access.sh @@ -0,0 +1,46 @@ +APP_NAME="{{app_name}}" +TENANT_ID="{{tenant_id}}" +BILLING_ACCOUNT_ID="{{billing_account_id}}" + +az login --tenant $TENANT_ID + +# Create app +APP_ID=$(az ad app create --display-name $APP_NAME --output json --query appId) +APP_ID=$(echo $APP_ID | tr -d '"') + +# Generate secret key +SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query password) +SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') + +# Create service principal +az ad sp create --id $APP_ID + +# Role assignments +# assign reservation reader role +az role assignment create --assignee $APP_ID --role "Reservations Reader" --scope "/providers/Microsoft.Capacity" + +# assign reservation purchaser role +az role assignment create --assignee $APP_ID --role "Reservations Purchaser" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign reservation administrator role +az role assignment create --assignee $APP_ID --role "Reservations Administrator" --scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits" + +# assign savings plan purchaser role +az role assignment create --assignee $APP_ID --role "Savings plan Purchaser" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign savings plan administrator role +az role assignment create --assignee $APP_ID --role "Savings plan Administrator" --scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign billing reader role +az role assignment create --assignee $APP_ID --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" + + +# Print registered app info +echo "App ID: $APP_ID" +echo "Secret Key: $SECRET_KEY" diff --git a/Utilities/AzureSetup/MCA/eco_azure_full_access.tf b/Utilities/AzureSetup/MCA/eco_azure_full_access.tf new file mode 100644 index 0000000..1de044d --- /dev/null +++ b/Utilities/AzureSetup/MCA/eco_azure_full_access.tf @@ -0,0 +1,103 @@ +variable "tenant_id" { + description = "The Tenant ID for the Azure subscription" + type = string + default = "" +} + +variable "billing_account_id" { + description = "The Billing Account ID" + type = string + default = "" +} + +# Provider block +provider "azuread" { + version = "3.0.0" +} +provider "azurerm" { + version = "3.0.0" + features {} +} +# Resource block +# Create an Azure AD application registration +resource "azuread_application_registration" "create_registered_app" { + display_name = "EcoAzureADApp3" +} +# Create an Azure AD service principal +resource "azuread_service_principal" "create_service_principal" { + client_id = azuread_application_registration.create_registered_app.client_id +} +# Create an Azure AD application password +resource "azuread_application_password" "create_secret_key" { + application_id = azuread_application_registration.create_registered_app.id +} + +# Role assignments +# assign reservation reader role +resource "null_resource" "reservation_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Reader" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign reservation purchaser role +resource "azurerm_role_assignment" "reservation_purchaser_role_assignment" { + role_definition_name = "Reservations Purchaser" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign reservation administrator role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Administrator" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign savings plan reader role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign savings plan purchaser role +resource "azurerm_role_assignment" "reservation_purchaser_role_assignment" { + role_definition_name = "Savings plan Purchaser" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign savings plan administrator role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Administrator" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign cost management reader role +resource "azurerm_role_assignment" "cost_management_reader_role_assignment" { + role_definition_name = "Cost Management Reader" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign billing reader role +resource "null_resource" "billing_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${var.billing_account_id}"' + } +} + +# Output +output "client_id" { + value = azuread_application_registration.create_registered_app.client_id +} +output "object_id" { + value = azuread_service_principal.create_service_principal.object_id +} +output "application_secret" { + value = azuread_application_password.create_secret_key.value + sensitive = true +} +data "azuread_client_config" "current" {} diff --git a/Utilities/AzureSetup/MCA/eco_azure_readonly.ps1 b/Utilities/AzureSetup/MCA/eco_azure_readonly.ps1 new file mode 100644 index 0000000..3551ebb --- /dev/null +++ b/Utilities/AzureSetup/MCA/eco_azure_readonly.ps1 @@ -0,0 +1,40 @@ +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" +$billingAccountId = "{{billing_account_id}}" + +# Connect to Azure AD +Connect-AzAccount -TenantId $tenantId + +# Register the app +$app = New-AzADApplication -DisplayName $appName + +# Create a service principal +New-AzADServicePrincipal -ApplicationId $app.AppId + +# Delete all secret keys +$secretKeys = Get-AzADAppCredential -ApplicationId $appId +foreach ($secretKey in $secretKeys) { + # Remove the secret key + Remove-AzADAppCredential -ApplicationId $appId -KeyId $secretKey.KeyId +} + +# Create a client secret +$secret = New-AzADAppCredential -ApplicationId $app.AppId -StartDate (Get-Date) -EndDate (Get-Date).AddYears(1) + +# Role assignments +# assign reservation reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Reader" -Scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Reader" -Scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign billing reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Billing Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" + + +# Output app details +Write-Host "App ID:" $app.AppId +Write-Host "Secret Key:" $secret.SecretText diff --git a/Utilities/AzureSetup/MCA/eco_azure_readonly.py b/Utilities/AzureSetup/MCA/eco_azure_readonly.py new file mode 100644 index 0000000..9d1cb47 --- /dev/null +++ b/Utilities/AzureSetup/MCA/eco_azure_readonly.py @@ -0,0 +1,80 @@ +import requests +from azure.identity import DefaultAzureCredential + +# Set up the necessary variables +TENANT_ID = "{{tenant_id}}" +APP_NAME = "{{app_name}}" +BILLING_ACCOUNT_ID = "{{billing_account_id}}" + +# Authenticate using DefaultAzureCredential +credential = DefaultAzureCredential() +graph_token = credential.get_token("https://graph.microsoft.com/.default") +management_token = credential.get_token("https://management.azure.com/.default") +graph_header = {"Authorization": f"Bearer {graph_token.token}", "Content-Type": "application/json"} +management_header = {"Authorization": f"Bearer {management_token.token}", "Content-Type": "application/json"} + +# Register the application +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications", json={"displayName": APP_NAME}) +app_id = resp.json()["appId"] + +# Remove the existing secret keys +resp = requests.get(f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')", headers=graph_header) +resp.raise_for_status() +keys = [pc["keyId"] for pc in resp.json()["passwordCredentials"]] +for key in keys: + delete_response = requests.post(url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/removePassword", headers=graph_header, json={"keyId": key}) + delete_response.raise_for_status() + +# Create the secret key +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/addPassword", json={"passwordCredential": {}}) +resp.raise_for_status() +secret_key = resp.json()["secretText"] + +# Create the service principal +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/servicePrincipals", json={"appId": app_id}) +resp.raise_for_status() +object_id = resp.json()["id"] + +# setup for role assignments +# assign reservation reader role +role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign cost management reader role +role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign billing reader role +role_definition_id = "50000000-aaaa-bbbb-cccc-100000000002" +scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + + +print(f"App ID: {app_id}") +print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/MCA/eco_azure_readonly.sh b/Utilities/AzureSetup/MCA/eco_azure_readonly.sh new file mode 100644 index 0000000..f5ed213 --- /dev/null +++ b/Utilities/AzureSetup/MCA/eco_azure_readonly.sh @@ -0,0 +1,34 @@ +APP_NAME="{{app_name}}" +TENANT_ID="{{tenant_id}}" +BILLING_ACCOUNT_ID="{{billing_account_id}}" + +az login --tenant $TENANT_ID + +# Create app +APP_ID=$(az ad app create --display-name $APP_NAME --output json --query appId) +APP_ID=$(echo $APP_ID | tr -d '"') + +# Generate secret key +SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query password) +SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') + +# Create service principal +az ad sp create --id $APP_ID + +# Role assignments +# assign reservation reader role +az role assignment create --assignee $APP_ID --role "Reservations Reader" --scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign billing reader role +az role assignment create --assignee $APP_ID --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" + + +# Print registered app info +echo "App ID: $APP_ID" +echo "Secret Key: $SECRET_KEY" diff --git a/Utilities/AzureSetup/MCA/eco_azure_readonly.tf b/Utilities/AzureSetup/MCA/eco_azure_readonly.tf new file mode 100644 index 0000000..c6c5755 --- /dev/null +++ b/Utilities/AzureSetup/MCA/eco_azure_readonly.tf @@ -0,0 +1,75 @@ +variable "tenant_id" { + description = "The Tenant ID for the Azure subscription" + type = string + default = "" +} + +variable "billing_account_id" { + description = "The Billing Account ID" + type = string + default = "" +} + +# Provider block +provider "azuread" { + version = "3.0.0" +} +provider "azurerm" { + version = "3.0.0" + features {} +} +# Resource block +# Create an Azure AD application registration +resource "azuread_application_registration" "create_registered_app" { + display_name = "EcoAzureADApp3" +} +# Create an Azure AD service principal +resource "azuread_service_principal" "create_service_principal" { + client_id = azuread_application_registration.create_registered_app.client_id +} +# Create an Azure AD application password +resource "azuread_application_password" "create_secret_key" { + application_id = azuread_application_registration.create_registered_app.id +} + +# Role assignments +# assign reservation reader role +resource "null_resource" "reservation_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Reader" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign savings plan reader role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign cost management reader role +resource "azurerm_role_assignment" "cost_management_reader_role_assignment" { + role_definition_name = "Cost Management Reader" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign billing reader role +resource "null_resource" "billing_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${var.billing_account_id}"' + } +} + +# Output +output "client_id" { + value = azuread_application_registration.create_registered_app.client_id +} +output "object_id" { + value = azuread_service_principal.create_service_principal.object_id +} +output "application_secret" { + value = azuread_application_password.create_secret_key.value + sensitive = true +} +data "azuread_client_config" "current" {} diff --git a/Utilities/AzureSetup/PAYG/eco_azure_full_access.ps1 b/Utilities/AzureSetup/PAYG/eco_azure_full_access.ps1 new file mode 100644 index 0000000..1f06d75 --- /dev/null +++ b/Utilities/AzureSetup/PAYG/eco_azure_full_access.ps1 @@ -0,0 +1,48 @@ +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" + +# Connect to Azure AD +Connect-AzAccount -TenantId $tenantId + +# Register the app +$app = New-AzADApplication -DisplayName $appName + +# Create a service principal +New-AzADServicePrincipal -ApplicationId $app.AppId + +# Delete all secret keys +$secretKeys = Get-AzADAppCredential -ApplicationId $appId +foreach ($secretKey in $secretKeys) { + # Remove the secret key + Remove-AzADAppCredential -ApplicationId $appId -KeyId $secretKey.KeyId +} + +# Create a client secret +$secret = New-AzADAppCredential -ApplicationId $app.AppId -StartDate (Get-Date) -EndDate (Get-Date).AddYears(1) + +# Role assignments +# assign reservation reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Reader" -Scope "/providers/Microsoft.Capacity" + +# assign reservation purchaser role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Purchaser" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign reservation administrator role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Administrator" -Scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Reader" -Scope "/providers/Microsoft.BillingBenefits" + +# assign savings plan purchaser role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Purchaser" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + +# assign savings plan administrator role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Administrator" -Scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + + +# Output app details +Write-Host "App ID:" $app.AppId +Write-Host "Secret Key:" $secret.SecretText diff --git a/Utilities/AzureSetup/PAYG/eco_azure_full_access.py b/Utilities/AzureSetup/PAYG/eco_azure_full_access.py new file mode 100644 index 0000000..0b87b75 --- /dev/null +++ b/Utilities/AzureSetup/PAYG/eco_azure_full_access.py @@ -0,0 +1,118 @@ +import requests +from azure.identity import DefaultAzureCredential + +# Set up the necessary variables +TENANT_ID = "{{tenant_id}}" +APP_NAME = "{{app_name}}" + +# Authenticate using DefaultAzureCredential +credential = DefaultAzureCredential() +graph_token = credential.get_token("https://graph.microsoft.com/.default") +management_token = credential.get_token("https://management.azure.com/.default") +graph_header = {"Authorization": f"Bearer {graph_token.token}", "Content-Type": "application/json"} +management_header = {"Authorization": f"Bearer {management_token.token}", "Content-Type": "application/json"} + +# Register the application +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications", json={"displayName": APP_NAME}) +app_id = resp.json()["appId"] + +# Remove the existing secret keys +resp = requests.get(f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')", headers=graph_header) +resp.raise_for_status() +keys = [pc["keyId"] for pc in resp.json()["passwordCredentials"]] +for key in keys: + delete_response = requests.post(url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/removePassword", headers=graph_header, json={"keyId": key}) + delete_response.raise_for_status() + +# Create the secret key +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/addPassword", json={"passwordCredential": {}}) +resp.raise_for_status() +secret_key = resp.json()["secretText"] + +# Create the service principal +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/servicePrincipals", json={"appId": app_id}) +resp.raise_for_status() +object_id = resp.json()["id"] + +# setup for role assignments +# assign reservation reader role +role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign reservation purchaser role +role_definition_id = "f7b75c60-3036-4b75-91c3-6b41c27c1689" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign reservation administrator role +role_definition_id = "a8889054-8d42-49c9-bc1c-52486c10e7cd" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign savings plan purchaser role +role_definition_id = "3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign savings plan administrator role +role_definition_id = "433febaf-a31d-4d4f-8dc8-b4593b39bda5" +scope = "providers/Microsoft.BillingBenefits" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign cost management reader role +role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + + +print(f"App ID: {app_id}") +print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/PAYG/eco_azure_full_access.sh b/Utilities/AzureSetup/PAYG/eco_azure_full_access.sh new file mode 100644 index 0000000..bc303e6 --- /dev/null +++ b/Utilities/AzureSetup/PAYG/eco_azure_full_access.sh @@ -0,0 +1,42 @@ +APP_NAME="{{app_name}}" +TENANT_ID="{{tenant_id}}" + +az login --tenant $TENANT_ID + +# Create app +APP_ID=$(az ad app create --display-name $APP_NAME --output json --query appId) +APP_ID=$(echo $APP_ID | tr -d '"') + +# Generate secret key +SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query password) +SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') + +# Create service principal +az ad sp create --id $APP_ID + +# Role assignments +# assign reservation reader role +az role assignment create --assignee $APP_ID --role "Reservations Reader" --scope "/providers/Microsoft.Capacity" + +# assign reservation purchaser role +az role assignment create --assignee $APP_ID --role "Reservations Purchaser" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign reservation administrator role +az role assignment create --assignee $APP_ID --role "Reservations Administrator" --scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits" + +# assign savings plan purchaser role +az role assignment create --assignee $APP_ID --role "Savings plan Purchaser" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + +# assign savings plan administrator role +az role assignment create --assignee $APP_ID --role "Savings plan Administrator" --scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + + +# Print registered app info +echo "App ID: $APP_ID" +echo "Secret Key: $SECRET_KEY" diff --git a/Utilities/AzureSetup/PAYG/eco_azure_full_access.tf b/Utilities/AzureSetup/PAYG/eco_azure_full_access.tf new file mode 100644 index 0000000..42b86c7 --- /dev/null +++ b/Utilities/AzureSetup/PAYG/eco_azure_full_access.tf @@ -0,0 +1,96 @@ +variable "tenant_id" { + description = "The Tenant ID for the Azure subscription" + type = string + default = "" +} + +variable "billing_account_id" { + description = "The Billing Account ID" + type = string + default = "" +} + +# Provider block +provider "azuread" { + version = "3.0.0" +} +provider "azurerm" { + version = "3.0.0" + features {} +} +# Resource block +# Create an Azure AD application registration +resource "azuread_application_registration" "create_registered_app" { + display_name = "EcoAzureADApp3" +} +# Create an Azure AD service principal +resource "azuread_service_principal" "create_service_principal" { + client_id = azuread_application_registration.create_registered_app.client_id +} +# Create an Azure AD application password +resource "azuread_application_password" "create_secret_key" { + application_id = azuread_application_registration.create_registered_app.id +} + +# Role assignments +# assign reservation reader role +resource "null_resource" "reservation_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Reader" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign reservation purchaser role +resource "azurerm_role_assignment" "reservation_purchaser_role_assignment" { + role_definition_name = "Reservations Purchaser" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign reservation administrator role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Administrator" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign savings plan reader role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign savings plan purchaser role +resource "azurerm_role_assignment" "reservation_purchaser_role_assignment" { + role_definition_name = "Savings plan Purchaser" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# assign savings plan administrator role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Administrator" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign cost management reader role +resource "azurerm_role_assignment" "cost_management_reader_role_assignment" { + role_definition_name = "Cost Management Reader" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# Output +output "client_id" { + value = azuread_application_registration.create_registered_app.client_id +} +output "object_id" { + value = azuread_service_principal.create_service_principal.object_id +} +output "application_secret" { + value = azuread_application_password.create_secret_key.value + sensitive = true +} +data "azuread_client_config" "current" {} diff --git a/Utilities/AzureSetup/PAYG/eco_azure_readonly.ps1 b/Utilities/AzureSetup/PAYG/eco_azure_readonly.ps1 new file mode 100644 index 0000000..1fda6cd --- /dev/null +++ b/Utilities/AzureSetup/PAYG/eco_azure_readonly.ps1 @@ -0,0 +1,36 @@ +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" + +# Connect to Azure AD +Connect-AzAccount -TenantId $tenantId + +# Register the app +$app = New-AzADApplication -DisplayName $appName + +# Create a service principal +New-AzADServicePrincipal -ApplicationId $app.AppId + +# Delete all secret keys +$secretKeys = Get-AzADAppCredential -ApplicationId $appId +foreach ($secretKey in $secretKeys) { + # Remove the secret key + Remove-AzADAppCredential -ApplicationId $appId -KeyId $secretKey.KeyId +} + +# Create a client secret +$secret = New-AzADAppCredential -ApplicationId $app.AppId -StartDate (Get-Date) -EndDate (Get-Date).AddYears(1) + +# Role assignments +# assign reservation reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Reservations Reader" -Scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Reader" -Scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" + + +# Output app details +Write-Host "App ID:" $app.AppId +Write-Host "Secret Key:" $secret.SecretText diff --git a/Utilities/AzureSetup/PAYG/eco_azure_readonly.py b/Utilities/AzureSetup/PAYG/eco_azure_readonly.py new file mode 100644 index 0000000..1d57a4e --- /dev/null +++ b/Utilities/AzureSetup/PAYG/eco_azure_readonly.py @@ -0,0 +1,66 @@ +import requests +from azure.identity import DefaultAzureCredential + +# Set up the necessary variables +TENANT_ID = "{{tenant_id}}" +APP_NAME = "{{app_name}}" + +# Authenticate using DefaultAzureCredential +credential = DefaultAzureCredential() +graph_token = credential.get_token("https://graph.microsoft.com/.default") +management_token = credential.get_token("https://management.azure.com/.default") +graph_header = {"Authorization": f"Bearer {graph_token.token}", "Content-Type": "application/json"} +management_header = {"Authorization": f"Bearer {management_token.token}", "Content-Type": "application/json"} + +# Register the application +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications", json={"displayName": APP_NAME}) +app_id = resp.json()["appId"] + +# Remove the existing secret keys +resp = requests.get(f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')", headers=graph_header) +resp.raise_for_status() +keys = [pc["keyId"] for pc in resp.json()["passwordCredentials"]] +for key in keys: + delete_response = requests.post(url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/removePassword", headers=graph_header, json={"keyId": key}) + delete_response.raise_for_status() + +# Create the secret key +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/applications(appId='{app_id}')/addPassword", json={"passwordCredential": {}}) +resp.raise_for_status() +secret_key = resp.json()["secretText"] + +# Create the service principal +resp = requests.post(headers=graph_header, url=f"https://graph.microsoft.com/v1.0/servicePrincipals", json={"appId": app_id}) +resp.raise_for_status() +object_id = resp.json()["id"] + +# setup for role assignments +# assign reservation reader role +role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" +scope = "providers/Microsoft.Capacity" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + +# assign cost management reader role +role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" +scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +data = { + "properties": { + "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", + "principalId": object_id + } +} +resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp.raise_for_status() + + +print(f"App ID: {app_id}") +print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/PAYG/eco_azure_readonly.sh b/Utilities/AzureSetup/PAYG/eco_azure_readonly.sh new file mode 100644 index 0000000..6a3340a --- /dev/null +++ b/Utilities/AzureSetup/PAYG/eco_azure_readonly.sh @@ -0,0 +1,30 @@ +APP_NAME="{{app_name}}" +TENANT_ID="{{tenant_id}}" + +az login --tenant $TENANT_ID + +# Create app +APP_ID=$(az ad app create --display-name $APP_NAME --output json --query appId) +APP_ID=$(echo $APP_ID | tr -d '"') + +# Generate secret key +SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query password) +SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') + +# Create service principal +az ad sp create --id $APP_ID + +# Role assignments +# assign reservation reader role +az role assignment create --assignee $APP_ID --role "Reservations Reader" --scope "/providers/Microsoft.Capacity" + +# assign savings plan reader role +az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits" + +# assign cost management reader role +az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" + + +# Print registered app info +echo "App ID: $APP_ID" +echo "Secret Key: $SECRET_KEY" diff --git a/Utilities/AzureSetup/PAYG/eco_azure_readonly.tf b/Utilities/AzureSetup/PAYG/eco_azure_readonly.tf new file mode 100644 index 0000000..28faf98 --- /dev/null +++ b/Utilities/AzureSetup/PAYG/eco_azure_readonly.tf @@ -0,0 +1,68 @@ +variable "tenant_id" { + description = "The Tenant ID for the Azure subscription" + type = string + default = "" +} + +variable "billing_account_id" { + description = "The Billing Account ID" + type = string + default = "" +} + +# Provider block +provider "azuread" { + version = "3.0.0" +} +provider "azurerm" { + version = "3.0.0" + features {} +} +# Resource block +# Create an Azure AD application registration +resource "azuread_application_registration" "create_registered_app" { + display_name = "EcoAzureADApp3" +} +# Create an Azure AD service principal +resource "azuread_service_principal" "create_service_principal" { + client_id = azuread_application_registration.create_registered_app.client_id +} +# Create an Azure AD application password +resource "azuread_application_password" "create_secret_key" { + application_id = azuread_application_registration.create_registered_app.id +} + +# Role assignments +# assign reservation reader role +resource "null_resource" "reservation_reader_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Reservations Reader" --scope "/providers/Microsoft.Capacity"' + } +} + +# assign savings plan reader role +resource "null_resource" "reservation_administrator_role_assignment" { + provisioner "local-exec" { + command = 'az role assignment create --assignee ${azuread_application_registration.create_registered_app.id} --role "Savings plan Reader" --scope "/providers/Microsoft.BillingBenefits"' + } +} + +# assign cost management reader role +resource "azurerm_role_assignment" "cost_management_reader_role_assignment" { + role_definition_name = "Cost Management Reader" + principal_id = azuread_service_principal.create_service_principal.id + scope = "/providers/Microsoft.Management/managementGroups/${var.tenant_id}" +} + +# Output +output "client_id" { + value = azuread_application_registration.create_registered_app.client_id +} +output "object_id" { + value = azuread_service_principal.create_service_principal.object_id +} +output "application_secret" { + value = azuread_application_password.create_secret_key.value + sensitive = true +} +data "azuread_client_config" "current" {} diff --git a/Utilities/AzureSetup/README.md b/Utilities/AzureSetup/README.md new file mode 100644 index 0000000..7f088fd --- /dev/null +++ b/Utilities/AzureSetup/README.md @@ -0,0 +1,33 @@ +# Azure Setup Scripts +This repository contains scripts to assist with setting up Eco Azure. The following Azure agreement types are supported: +* Microsoft CSP (Cloud Solution Provider) + * Full Access + * Read Only +* Enterprise Agreement + * Full Access + * Read Only +* Microsoft Customer Agreement (MCA) + * Full Access + * Read Only +* Pay-As-You-Go + * Full Access + * Read Only + +## Usage +1. Chose an appropriate script to run based on permissions level and Azure agreement type. +2. Update the following variables to values appropriate for your environment: +``` +$appName = "{{app_name}}" +$tenantId = "{{tenant_id}}" +$billingAccountId = "{{billing_account_id}}" +``` +3. If using the provided python scripts: install the requirements using pip +``` +pip install -r requirements.txt +``` +4. Execute the script +``` +# For example +python PAYG/eco_azure_full_access.py +``` + diff --git a/Utilities/AzureSetup/requirements.txt b/Utilities/AzureSetup/requirements.txt new file mode 100644 index 0000000..1ecbad7 --- /dev/null +++ b/Utilities/AzureSetup/requirements.txt @@ -0,0 +1,2 @@ +azure-identity==1.13.0 +requests==2.31.0 From 0a103eb99231f2e5a5dea1352a48d929a5e05723 Mon Sep 17 00:00:00 2001 From: Jeff Snyder Date: Wed, 9 Jul 2025 11:01:48 -0400 Subject: [PATCH 14/14] [src] SF-23264: Update Eco Azure setup scripts and add postman collection (#107) --- .../AzureSetup.postman_collection.json | 464 ++++++++++++++++++ .../AzureSetup/CSP/eco_azure_full_access.ps1 | 27 +- .../AzureSetup/CSP/eco_azure_full_access.py | 23 +- .../AzureSetup/CSP/eco_azure_full_access.sh | 15 +- .../AzureSetup/CSP/eco_azure_readonly.ps1 | 27 +- .../AzureSetup/CSP/eco_azure_readonly.py | 15 +- .../AzureSetup/CSP/eco_azure_readonly.sh | 15 +- .../AzureSetup/EA/eco_azure_full_access.ps1 | 28 +- .../AzureSetup/EA/eco_azure_full_access.py | 21 +- .../AzureSetup/EA/eco_azure_full_access.sh | 16 +- .../AzureSetup/EA/eco_azure_readonly.ps1 | 28 +- Utilities/AzureSetup/EA/eco_azure_readonly.py | 12 +- Utilities/AzureSetup/EA/eco_azure_readonly.sh | 16 +- .../AzureSetup/MCA/eco_azure_full_access.ps1 | 26 +- .../AzureSetup/MCA/eco_azure_full_access.py | 23 +- .../AzureSetup/MCA/eco_azure_full_access.sh | 15 +- .../AzureSetup/MCA/eco_azure_readonly.ps1 | 26 +- .../AzureSetup/MCA/eco_azure_readonly.py | 15 +- .../AzureSetup/MCA/eco_azure_readonly.sh | 15 +- .../AzureSetup/PAYG/eco_azure_full_access.py | 13 +- .../AzureSetup/PAYG/eco_azure_readonly.py | 5 +- 21 files changed, 743 insertions(+), 102 deletions(-) create mode 100644 Utilities/AzureSetup/AzureSetup.postman_collection.json diff --git a/Utilities/AzureSetup/AzureSetup.postman_collection.json b/Utilities/AzureSetup/AzureSetup.postman_collection.json new file mode 100644 index 0000000..d42b59c --- /dev/null +++ b/Utilities/AzureSetup/AzureSetup.postman_collection.json @@ -0,0 +1,464 @@ +{ + "info": { + "_postman_id": "92aa972e-8f3a-4984-84fd-84c1c5ca4d4a", + "name": "Eco Azure Onboarding", + "description": "The list of APIs Rest Endpoints for completing onboarding to the Eco Azure product. Includes tasks like the following:\n\n- Create an App Registration\n \n- Create an App Registration Secret\n \n- Create an App Registration Service Principal\n \n- Create Role Assignments to the Registered App\n \n - Role for Reading Reservations\n \n - Role for Reading Savings Plans\n \n - Role for Tenanat Cost and Billing Reader\n \n - Role for Billing account Read (for EA customers)\n \n - Role for Billing profiles Read (for MCA customers)\n \n - Role for managing existing Reservations (for paying customers)\n \n - Role for managing existing Savings Plans (for paying customers)\n \n - Role for purchasing new Reservations (for paying customers)\n \n - Role for purchasing new Savings Plans (for paying customers)", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", + "_exporter_id": "2995178" + }, + "item": [ + { + "name": "Create App Registration", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{graph_bearer_token}}" + } + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\"displayName\": \"{{app_display_name}}\"}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{graph_api}}/v1.0/applications" + }, + "response": [] + }, + { + "name": "Create Service Principal", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{graph_bearer_token}}" + } + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\"appId\": \"{{app_id}}\"}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{graph_api}}/v1.0/servicePrincipals" + }, + "response": [] + }, + { + "name": "Create Secret Key", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{graph_bearer_token}}" + } + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\"passwordCredential\": {}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{graph_api}}/v1.0/applications(appId='{{app_id}}')/addPassword" + }, + "response": [] + }, + { + "name": "Assign Cost Management Reader", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{management_bearer_token}}" + } + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"properties\": {\n \"roleDefinitionId\": \"providers/Microsoft.Management/managementGroups/{{tenant_id}}/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3\",\n \"principalId\": \"{{service_principal_id}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{management_api}}/providers/Microsoft.Management/managementGroups/{{tenant_id}}/providers/Microsoft.Authorization/roleAssignments/{{$guid}}?api-version=2022-04-01", + "host": [ + "{{management_api}}" + ], + "path": [ + "providers", + "Microsoft.Management", + "managementGroups", + "{{tenant_id}}", + "providers", + "Microsoft.Authorization", + "roleAssignments", + "{{$guid}}" + ], + "query": [ + { + "key": "api-version", + "value": "2022-04-01" + } + ] + } + }, + "response": [] + }, + { + "name": "Assign Reservation Reader", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{management_bearer_token}}" + } + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"properties\": {\n \"roleDefinitionId\": \"providers/Microsoft.Capacity/providers/Microsoft.Authorization/roleDefinitions/582fc458-8989-419f-a480-75249bc5db7e\",\n \"principalId\": \"{{service_principal_id}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{management_api}}/providers/Microsoft.Capacity/providers/Microsoft.Authorization/roleAssignments/{{$guid}}?api-version=2022-04-01", + "host": [ + "{{management_api}}" + ], + "path": [ + "providers", + "Microsoft.Capacity", + "providers", + "Microsoft.Authorization", + "roleAssignments", + "{{$guid}}" + ], + "query": [ + { + "key": "api-version", + "value": "2022-04-01" + } + ] + } + }, + "response": [] + }, + { + "name": "Assign Reservation Purchaser (Paying Customers Only)", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{management_bearer_token}}" + } + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"properties\": {\n \"roleDefinitionId\": \"providers/Microsoft.Management/managementGroups/{{tenant_id}}/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689\",\n \"principalId\": \"{{service_principal_id}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{management_api}}/providers/Microsoft.Management/managementGroups/{{tenant_id}}/providers/Microsoft.Authorization/roleAssignments/{{$guid}}?api-version=2022-04-01", + "host": [ + "{{management_api}}" + ], + "path": [ + "providers", + "Microsoft.Management", + "managementGroups", + "{{tenant_id}}", + "providers", + "Microsoft.Authorization", + "roleAssignments", + "{{$guid}}" + ], + "query": [ + { + "key": "api-version", + "value": "2022-04-01" + } + ] + } + }, + "response": [] + }, + { + "name": "Assign Savings Plan Purchaser (Paying Customers Only)", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{management_bearer_token}}" + } + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"properties\": {\n \"roleDefinitionId\": \"providers/Microsoft.Management/managementGroups/{{tenant_id}}/providers/Microsoft.Authorization/roleDefinitions/3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74\",\n \"principalId\": \"{{service_principal_id}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{management_api}}/providers/Microsoft.Management/managementGroups/{{tenant_id}}/providers/Microsoft.Authorization/roleAssignments/{{$guid}}?api-version=2022-04-01", + "host": [ + "{{management_api}}" + ], + "path": [ + "providers", + "Microsoft.Management", + "managementGroups", + "{{tenant_id}}", + "providers", + "Microsoft.Authorization", + "roleAssignments", + "{{$guid}}" + ], + "query": [ + { + "key": "api-version", + "value": "2022-04-01" + } + ] + } + }, + "response": [] + }, + { + "name": "Assign Billing Reader (MCA and CSP Customers Only)", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{management_bearer_token}}" + } + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"Properties\": {\n \"RoleDefinitionId\": \"providers/Microsoft.Billing/billingAccounts/{{billing_account_id}}/billingRoleDefinitions/50000000-aaaa-bbbb-cccc-100000000002\",\n \"PrincipalId\": \"{{service_principal_id}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{management_api}}/providers/Microsoft.Billing/billingAccounts/{{billing_account_id}}/createBillingRoleAssignment?api-version=2019-10-01-preview", + "host": [ + "{{management_api}}" + ], + "path": [ + "providers", + "Microsoft.Billing", + "billingAccounts", + "{{billing_account_id}}", + "createBillingRoleAssignment" + ], + "query": [ + { + "key": "api-version", + "value": "2019-10-01-preview" + } + ] + } + }, + "response": [] + }, + { + "name": "Assign Enrollment Reader (EA Customers Only)", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{management_bearer_token}}" + } + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"properties\": {\n \"roleDefinitionId\": \"providers/Microsoft.Billing/billingAccounts/{{billing_account_id}}/billingRoleDefinitions/24f8edb6-1668-4659-b5e2-40bb5f3a7d7e\",\n \"principalId\": \"{{service_principal_id}}\",\n \"principalTenantId\": \"{{tenant_id}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{management_api}}/providers/Microsoft.Billing/billingAccounts/{{billing_account_id}}/billingRoleAssignments/{{$guid}}?api-version=2019-10-01-preview", + "host": [ + "{{management_api}}" + ], + "path": [ + "providers", + "Microsoft.Billing", + "billingAccounts", + "{{billing_account_id}}", + "billingRoleAssignments", + "{{$guid}}" + ], + "query": [ + { + "key": "api-version", + "value": "2019-10-01-preview" + } + ] + } + }, + "response": [] + }, + { + "name": "Assign Reservation Administrator (Paying Customers Only)", + "request": { + "auth": { + "type": "bearer", + "bearer": { + "token": "{{management_bearer_token}}" + } + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"properties\": {\n \"roleDefinitionId\": \"providers/Microsoft.Capacity/providers/Microsoft.Authorization/roleDefinitions/a8889054-8d42-49c9-bc1c-52486c10e7cd\",\n \"principalId\": \"{{service_principal_id}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{management_api}}/providers/Microsoft.Capacity/providers/Microsoft.Authorization/roleAssignments/{{$guid}}?api-version=2022-04-01", + "host": [ + "{{management_api}}" + ], + "path": [ + "providers", + "Microsoft.Capacity", + "providers", + "Microsoft.Authorization", + "roleAssignments", + "{{$guid}}" + ], + "query": [ + { + "key": "api-version", + "value": "2022-04-01" + } + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "management_bearer_token", + "value": "", + "type": "string" + }, + { + "key": "graph_bearer_token", + "value": "", + "type": "string" + }, + { + "key": "graph_api", + "value": "https://graph.microsoft.com", + "type": "string" + }, + { + "key": "management_api", + "value": "https://management.azure.com", + "type": "string" + }, + { + "key": "app_display_name", + "value": "EcoAzureConnection", + "type": "string" + }, + { + "key": "app_id", + "value": "", + "type": "string" + }, + { + "key": "service_principal_id", + "value": "", + "type": "string" + }, + { + "key": "tenant_id", + "value": "", + "type": "string" + }, + { + "key": "billing_account_id", + "value": "", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/Utilities/AzureSetup/CSP/eco_azure_full_access.ps1 b/Utilities/AzureSetup/CSP/eco_azure_full_access.ps1 index 4577210..bcb6018 100644 --- a/Utilities/AzureSetup/CSP/eco_azure_full_access.ps1 +++ b/Utilities/AzureSetup/CSP/eco_azure_full_access.ps1 @@ -8,8 +8,9 @@ Connect-AzAccount -TenantId $tenantId # Register the app $app = New-AzADApplication -DisplayName $appName -# Create a service principal -New-AzADServicePrincipal -ApplicationId $app.AppId +# Create a service principal and get its ID +$sp = New-AzADServicePrincipal -ApplicationId $app.AppId +$principalId = $sp.Id # Delete all secret keys $secretKeys = Get-AzADAppCredential -ApplicationId $appId @@ -43,8 +44,26 @@ New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Adm # assign cost management reader role New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" -# assign billing reader role -New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Billing Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" +# assign billing reader role via REST API +$ROLE_DEF_ID = "50000000-aaaa-bbbb-cccc-100000000002" +$API_VERSION = "2019-10-01-preview" +$SCOPE = "providers/Microsoft.Billing/billingAccounts/$billingAccountId" +$ACCESS_TOKEN = az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv +$DATA = @{ + Properties = @{ + RoleDefinitionId = "/$SCOPE/billingRoleDefinitions/$ROLE_DEF_ID" + PrincipalId = $principalId + } +} | ConvertTo-Json +$headers = @{ + "Content-Type" = "application/json" + "Authorization" = "Bearer $ACCESS_TOKEN" +} +Invoke-RestMethod -Method Post ` + -Uri "https://management.azure.com/$SCOPE/createBillingRoleAssignment?api-version=$API_VERSION" ` + -Headers $headers ` + -Body $DATA + # Output app details Write-Host "App ID:" $app.AppId diff --git a/Utilities/AzureSetup/CSP/eco_azure_full_access.py b/Utilities/AzureSetup/CSP/eco_azure_full_access.py index 8c3e202..44b22f3 100644 --- a/Utilities/AzureSetup/CSP/eco_azure_full_access.py +++ b/Utilities/AzureSetup/CSP/eco_azure_full_access.py @@ -1,5 +1,6 @@ import requests from azure.identity import DefaultAzureCredential +import uuid # Set up the necessary variables TENANT_ID = "{{tenant_id}}" @@ -39,7 +40,7 @@ # assign reservation reader role role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -52,7 +53,7 @@ # assign reservation purchaser role role_definition_id = "f7b75c60-3036-4b75-91c3-6b41c27c1689" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -65,7 +66,7 @@ # assign reservation administrator role role_definition_id = "a8889054-8d42-49c9-bc1c-52486c10e7cd" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -78,7 +79,7 @@ # assign savings plan purchaser role role_definition_id = "3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -91,7 +92,7 @@ # assign savings plan administrator role role_definition_id = "433febaf-a31d-4d4f-8dc8-b4593b39bda5" scope = "providers/Microsoft.BillingBenefits" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -104,7 +105,7 @@ # assign cost management reader role role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -117,14 +118,14 @@ # assign billing reader role role_definition_id = "50000000-aaaa-bbbb-cccc-100000000002" scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment?api-version=2019-10-01-preview" data = { - "properties": { - "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", - "principalId": object_id + "Properties": { + "RoleDefinitionId": f"{scope}/billingRoleDefinitions/{role_definition_id}", + "PrincipalId": object_id } } -resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp = requests.post(url=role_assignments_url, headers=management_header, json=data) resp.raise_for_status() diff --git a/Utilities/AzureSetup/CSP/eco_azure_full_access.sh b/Utilities/AzureSetup/CSP/eco_azure_full_access.sh index b883de3..b9965da 100644 --- a/Utilities/AzureSetup/CSP/eco_azure_full_access.sh +++ b/Utilities/AzureSetup/CSP/eco_azure_full_access.sh @@ -13,7 +13,7 @@ SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query passw SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') # Create service principal -az ad sp create --id $APP_ID +PRINCIPAL_ID=$(az ad sp create --id $APP_ID --output json --query id | tr -d '"') # Role assignments # assign reservation reader role @@ -37,8 +37,17 @@ az role assignment create --assignee $APP_ID --role "Savings plan Administrator" # assign cost management reader role az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" -# assign billing reader role -az role assignment create --assignee $APP_ID --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +# assign Billing Reader role using REST API +ROLE_DEF_ID="50000000-aaaa-bbbb-cccc-100000000002" +API_VERSION="2019-10-01-preview" +SCOPE="providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +ACCESS_TOKEN=$(az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv) +DATA='{\"Properties\": {\"RoleDefinitionId\": \"/${SCOPE}/billingRoleDefinitions/${ROLE_DEF_ID}\", \"PrincipalId\": \"${PRINCIPAL_ID}\"}}' +curl -X POST \ + "https://management.azure.com/${SCOPE}/createBillingRoleAssignment?api-version=${API_VERSION}" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -d "${DATA}" # Print registered app info diff --git a/Utilities/AzureSetup/CSP/eco_azure_readonly.ps1 b/Utilities/AzureSetup/CSP/eco_azure_readonly.ps1 index c347182..4aec719 100644 --- a/Utilities/AzureSetup/CSP/eco_azure_readonly.ps1 +++ b/Utilities/AzureSetup/CSP/eco_azure_readonly.ps1 @@ -8,8 +8,9 @@ Connect-AzAccount -TenantId $tenantId # Register the app $app = New-AzADApplication -DisplayName $appName -# Create a service principal -New-AzADServicePrincipal -ApplicationId $app.AppId +# Create a service principal and get its ID +$sp = New-AzADServicePrincipal -ApplicationId $app.AppId +$principalId = $sp.Id # Delete all secret keys $secretKeys = Get-AzADAppCredential -ApplicationId $appId @@ -31,9 +32,25 @@ New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Rea # assign cost management reader role New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" -# assign billing reader role -New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Billing Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId " - +# assign billing reader role via REST API +$ROLE_DEF_ID = "50000000-aaaa-bbbb-cccc-100000000002" +$API_VERSION = "2019-10-01-preview" +$SCOPE = "providers/Microsoft.Billing/billingAccounts/$billingAccountId" +$ACCESS_TOKEN = az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv +$DATA = @{ + Properties = @{ + RoleDefinitionId = "/$SCOPE/billingRoleDefinitions/$ROLE_DEF_ID" + PrincipalId = $principalId + } +} | ConvertTo-Json +$headers = @{ + "Content-Type" = "application/json" + "Authorization" = "Bearer $ACCESS_TOKEN" +} +Invoke-RestMethod -Method Post ` + -Uri "https://management.azure.com/$SCOPE/createBillingRoleAssignment?api-version=$API_VERSION" ` + -Headers $headers ` + -Body $DATA # Output app details Write-Host "App ID:" $app.AppId diff --git a/Utilities/AzureSetup/CSP/eco_azure_readonly.py b/Utilities/AzureSetup/CSP/eco_azure_readonly.py index ed15958..4cda430 100644 --- a/Utilities/AzureSetup/CSP/eco_azure_readonly.py +++ b/Utilities/AzureSetup/CSP/eco_azure_readonly.py @@ -1,5 +1,6 @@ import requests from azure.identity import DefaultAzureCredential +import uuid from Utilities.AzureSetup.CSP.eco_azure_full_access import BILLING_ACCOUNT_ID @@ -41,7 +42,7 @@ # assign reservation reader role role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -54,7 +55,7 @@ # assign cost management reader role role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -67,14 +68,14 @@ # assign billing reader role role_definition_id = "50000000-aaaa-bbbb-cccc-100000000002" scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment?api-version=2019-10-01-preview" data = { - "properties": { - "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", - "principalId": object_id + "Properties": { + "RoleDefinitionId": f"{scope}/billingRoleDefinitions/{role_definition_id}", + "PrincipalId": object_id } } -resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp = requests.post(url=role_assignments_url, headers=management_header, json=data) resp.raise_for_status() diff --git a/Utilities/AzureSetup/CSP/eco_azure_readonly.sh b/Utilities/AzureSetup/CSP/eco_azure_readonly.sh index f5ed213..1002ee8 100644 --- a/Utilities/AzureSetup/CSP/eco_azure_readonly.sh +++ b/Utilities/AzureSetup/CSP/eco_azure_readonly.sh @@ -13,7 +13,7 @@ SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query passw SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') # Create service principal -az ad sp create --id $APP_ID +PRINCIPAL_ID=$(az ad sp create --id $APP_ID --output json --query id | tr -d '"') # Role assignments # assign reservation reader role @@ -25,8 +25,17 @@ az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scop # assign cost management reader role az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" -# assign billing reader role -az role assignment create --assignee $APP_ID --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +# assign Billing Reader role using REST API +ROLE_DEF_ID="50000000-aaaa-bbbb-cccc-100000000002" +API_VERSION="2019-10-01-preview" +SCOPE="providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +ACCESS_TOKEN=$(az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv) +DATA='{\"Properties\": {\"RoleDefinitionId\": \"/${SCOPE}/billingRoleDefinitions/${ROLE_DEF_ID}\", \"PrincipalId\": \"${PRINCIPAL_ID}\"}}' +curl -X POST \ + "https://management.azure.com/${SCOPE}/createBillingRoleAssignment?api-version=${API_VERSION}" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -d "${DATA}" # Print registered app info diff --git a/Utilities/AzureSetup/EA/eco_azure_full_access.ps1 b/Utilities/AzureSetup/EA/eco_azure_full_access.ps1 index c6a11bd..56815f3 100644 --- a/Utilities/AzureSetup/EA/eco_azure_full_access.ps1 +++ b/Utilities/AzureSetup/EA/eco_azure_full_access.ps1 @@ -8,8 +8,9 @@ Connect-AzAccount -TenantId $tenantId # Register the app $app = New-AzADApplication -DisplayName $appName -# Create a service principal -New-AzADServicePrincipal -ApplicationId $app.AppId +# Create a service principal and get its ID +$sp = New-AzADServicePrincipal -ApplicationId $app.AppId +$principalId = $sp.Id # Delete all secret keys $secretKeys = Get-AzADAppCredential -ApplicationId $appId @@ -43,8 +44,27 @@ New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Adm # assign cost management reader role New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" -# assign enrollment reader role -New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Enrollment Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" +# Generate a UUID for the role assignment ID +$ROLE_ASSIGNMENT_ID = [guid]::NewGuid().ToString().ToLower() +$ROLE_DEF_ID = "24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" +$API_VERSION = "2019-10-01-preview" +$SCOPE = "providers/Microsoft.Billing/billingAccounts/$billingAccountId" +$ACCESS_TOKEN = az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv +$DATA = @{ + properties = @{ + roleDefinitionId = "/$SCOPE/billingRoleDefinitions/$ROLE_DEF_ID" + principalTenantId = $tenantId + principalId = $principalId + } +} | ConvertTo-Json +$headers = @{ + "Content-Type" = "application/json" + "Authorization" = "Bearer $ACCESS_TOKEN" +} +Invoke-RestMethod -Method Put ` + -Uri "https://management.azure.com/$SCOPE/billingRoleAssignments/$ROLE_ASSIGNMENT_ID?api-version=$API_VERSION" ` + -Headers $headers ` + -Body $DATA # Output app details diff --git a/Utilities/AzureSetup/EA/eco_azure_full_access.py b/Utilities/AzureSetup/EA/eco_azure_full_access.py index cc13584..90c3537 100644 --- a/Utilities/AzureSetup/EA/eco_azure_full_access.py +++ b/Utilities/AzureSetup/EA/eco_azure_full_access.py @@ -1,5 +1,6 @@ import requests from azure.identity import DefaultAzureCredential +import uuid # Set up the necessary variables TENANT_ID = "{{tenant_id}}" @@ -39,7 +40,7 @@ # assign reservation reader role role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -52,7 +53,7 @@ # assign reservation purchaser role role_definition_id = "f7b75c60-3036-4b75-91c3-6b41c27c1689" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -65,7 +66,7 @@ # assign reservation administrator role role_definition_id = "a8889054-8d42-49c9-bc1c-52486c10e7cd" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -78,7 +79,7 @@ # assign savings plan purchaser role role_definition_id = "3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -91,7 +92,7 @@ # assign savings plan administrator role role_definition_id = "433febaf-a31d-4d4f-8dc8-b4593b39bda5" scope = "providers/Microsoft.BillingBenefits" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -104,7 +105,7 @@ # assign cost management reader role role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -117,16 +118,16 @@ # assign enrollment reader role role_definition_id = "24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/billingRoleAssignments/{str(uuid.uuid4())}?api-version=2019-10-01-preview" data = { "properties": { - "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", - "principalId": object_id + "roleDefinitionId": f"{scope}/billingRoleDefinitions/{role_definition_id}", + "principalId": object_id, + "principalTenantId": TENANT_ID, } } resp = requests.put(url=role_assignments_url, headers=management_header, json=data) resp.raise_for_status() - print(f"App ID: {app_id}") print(f"Secret Key: {secret_key}") diff --git a/Utilities/AzureSetup/EA/eco_azure_full_access.sh b/Utilities/AzureSetup/EA/eco_azure_full_access.sh index f03f8f7..3ee88a2 100644 --- a/Utilities/AzureSetup/EA/eco_azure_full_access.sh +++ b/Utilities/AzureSetup/EA/eco_azure_full_access.sh @@ -14,7 +14,7 @@ SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query passw SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') # Create service principal -az ad sp create --id $APP_ID +PRINCIPAL_ID=$(az ad sp create --id $APP_ID --output json --query id | tr -d '"') # Role assignments # assign reservation reader role @@ -38,8 +38,18 @@ az role assignment create --assignee $APP_ID --role "Savings plan Administrator" # assign cost management reader role az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" -# assign enrollment reader role -az role assignment create --assignee $APP_ID --role "Enrollment Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +# assign Enrollment Reader role using REST API +ROLE_ASSIGNMENT_ID=$(uuidgen | tr 'A-F' 'a-f') +ROLE_DEF_ID="24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" +API_VERSION="2019-10-01-preview" +SCOPE="providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +ACCESS_TOKEN=$(az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv) +DATA='{\"properties\": {\"roleDefinitionId\": \"/${SCOPE}/billingRoleDefinitions/${ROLE_DEF_ID}\", \"principalTenantId\": \"${TENANT_ID}\", \"principalId\": \"${PRINCIPAL_ID}\"}}' +curl -X PUT \ + "https://management.azure.com/${SCOPE}/billingRoleAssignments/${ROLE_ASSIGNMENT_ID}?api-version=${API_VERSION}" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -d "${DATA}" # Print registered app info diff --git a/Utilities/AzureSetup/EA/eco_azure_readonly.ps1 b/Utilities/AzureSetup/EA/eco_azure_readonly.ps1 index a0e8f78..f2ad388 100644 --- a/Utilities/AzureSetup/EA/eco_azure_readonly.ps1 +++ b/Utilities/AzureSetup/EA/eco_azure_readonly.ps1 @@ -8,8 +8,9 @@ Connect-AzAccount -TenantId $tenantId # Register the app $app = New-AzADApplication -DisplayName $appName -# Create a service principal -New-AzADServicePrincipal -ApplicationId $app.AppId +# Create a service principal and get its ID +$sp = New-AzADServicePrincipal -ApplicationId $app.AppId +$principalId = $sp.Id # Delete all secret keys $secretKeys = Get-AzADAppCredential -ApplicationId $appId @@ -31,8 +32,27 @@ New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Rea # assign cost management reader role New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" -# assign enrollment reader role -New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Enrollment Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" +# Generate a UUID for the role assignment ID +$ROLE_ASSIGNMENT_ID = [guid]::NewGuid().ToString().ToLower() +$ROLE_DEF_ID = "24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" +$API_VERSION = "2019-10-01-preview" +$SCOPE = "providers/Microsoft.Billing/billingAccounts/$billingAccountId" +$ACCESS_TOKEN = az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv +$DATA = @{ + properties = @{ + roleDefinitionId = "/$SCOPE/billingRoleDefinitions/$ROLE_DEF_ID" + principalTenantId = $tenantId + principalId = $principalId + } +} | ConvertTo-Json +$headers = @{ + "Content-Type" = "application/json" + "Authorization" = "Bearer $ACCESS_TOKEN" +} +Invoke-RestMethod -Method Put ` + -Uri "https://management.azure.com/$SCOPE/billingRoleAssignments/$ROLE_ASSIGNMENT_ID?api-version=$API_VERSION" ` + -Headers $headers ` + -Body $DATA # Output app details diff --git a/Utilities/AzureSetup/EA/eco_azure_readonly.py b/Utilities/AzureSetup/EA/eco_azure_readonly.py index 3de574f..45f3c93 100644 --- a/Utilities/AzureSetup/EA/eco_azure_readonly.py +++ b/Utilities/AzureSetup/EA/eco_azure_readonly.py @@ -1,5 +1,6 @@ import requests from azure.identity import DefaultAzureCredential +import uuid # Set up the necessary variables TENANT_ID = "{{tenant_id}}" @@ -39,7 +40,7 @@ # assign reservation reader role role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -52,7 +53,7 @@ # assign cost management reader role role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -65,11 +66,12 @@ # assign enrollment reader role role_definition_id = "24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/billingRoleAssignments/{str(uuid.uuid4())}?api-version=2019-10-01-preview" data = { "properties": { - "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", - "principalId": object_id + "roleDefinitionId": f"{scope}/billingRoleDefinitions/{role_definition_id}", + "principalId": object_id, + "principalTenantId": TENANT_ID, } } resp = requests.put(url=role_assignments_url, headers=management_header, json=data) diff --git a/Utilities/AzureSetup/EA/eco_azure_readonly.sh b/Utilities/AzureSetup/EA/eco_azure_readonly.sh index 920602c..8606866 100644 --- a/Utilities/AzureSetup/EA/eco_azure_readonly.sh +++ b/Utilities/AzureSetup/EA/eco_azure_readonly.sh @@ -13,7 +13,7 @@ SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query passw SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') # Create service principal -az ad sp create --id $APP_ID +PRINCIPAL_ID=$(az ad sp create --id $APP_ID --output json --query id | tr -d '"') # Role assignments # assign reservation reader role @@ -25,8 +25,18 @@ az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scop # assign cost management reader role az role assignment create --assignee "{{app_id}}" --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" -# assign enrollment reader role -az role assignment create --assignee "{{app_id}}" --role "Enrollment Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +# assign Enrollment Reader role using REST API +ROLE_ASSIGNMENT_ID=$(uuidgen | tr 'A-F' 'a-f') +ROLE_DEF_ID="24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" +API_VERSION="2019-10-01-preview" +SCOPE="providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +ACCESS_TOKEN=$(az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv) +DATA='{\"properties\": {\"roleDefinitionId\": \"/${SCOPE}/billingRoleDefinitions/${ROLE_DEF_ID}\", \"principalTenantId\": \"${TENANT_ID}\", \"principalId\": \"${PRINCIPAL_ID}\"}}' +curl -X PUT \ + "https://management.azure.com/${SCOPE}/billingRoleAssignments/${ROLE_ASSIGNMENT_ID}?api-version=${API_VERSION}" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -d "${DATA}" # Print registered app info diff --git a/Utilities/AzureSetup/MCA/eco_azure_full_access.ps1 b/Utilities/AzureSetup/MCA/eco_azure_full_access.ps1 index ffd6e1d..bcb6018 100644 --- a/Utilities/AzureSetup/MCA/eco_azure_full_access.ps1 +++ b/Utilities/AzureSetup/MCA/eco_azure_full_access.ps1 @@ -8,8 +8,9 @@ Connect-AzAccount -TenantId $tenantId # Register the app $app = New-AzADApplication -DisplayName $appName -# Create a service principal -New-AzADServicePrincipal -ApplicationId $app.AppId +# Create a service principal and get its ID +$sp = New-AzADServicePrincipal -ApplicationId $app.AppId +$principalId = $sp.Id # Delete all secret keys $secretKeys = Get-AzADAppCredential -ApplicationId $appId @@ -43,8 +44,25 @@ New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Adm # assign cost management reader role New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" -# assign billing reader role -New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Billing Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" +# assign billing reader role via REST API +$ROLE_DEF_ID = "50000000-aaaa-bbbb-cccc-100000000002" +$API_VERSION = "2019-10-01-preview" +$SCOPE = "providers/Microsoft.Billing/billingAccounts/$billingAccountId" +$ACCESS_TOKEN = az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv +$DATA = @{ + Properties = @{ + RoleDefinitionId = "/$SCOPE/billingRoleDefinitions/$ROLE_DEF_ID" + PrincipalId = $principalId + } +} | ConvertTo-Json +$headers = @{ + "Content-Type" = "application/json" + "Authorization" = "Bearer $ACCESS_TOKEN" +} +Invoke-RestMethod -Method Post ` + -Uri "https://management.azure.com/$SCOPE/createBillingRoleAssignment?api-version=$API_VERSION" ` + -Headers $headers ` + -Body $DATA # Output app details diff --git a/Utilities/AzureSetup/MCA/eco_azure_full_access.py b/Utilities/AzureSetup/MCA/eco_azure_full_access.py index 8c3e202..44b22f3 100644 --- a/Utilities/AzureSetup/MCA/eco_azure_full_access.py +++ b/Utilities/AzureSetup/MCA/eco_azure_full_access.py @@ -1,5 +1,6 @@ import requests from azure.identity import DefaultAzureCredential +import uuid # Set up the necessary variables TENANT_ID = "{{tenant_id}}" @@ -39,7 +40,7 @@ # assign reservation reader role role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -52,7 +53,7 @@ # assign reservation purchaser role role_definition_id = "f7b75c60-3036-4b75-91c3-6b41c27c1689" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -65,7 +66,7 @@ # assign reservation administrator role role_definition_id = "a8889054-8d42-49c9-bc1c-52486c10e7cd" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -78,7 +79,7 @@ # assign savings plan purchaser role role_definition_id = "3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -91,7 +92,7 @@ # assign savings plan administrator role role_definition_id = "433febaf-a31d-4d4f-8dc8-b4593b39bda5" scope = "providers/Microsoft.BillingBenefits" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -104,7 +105,7 @@ # assign cost management reader role role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -117,14 +118,14 @@ # assign billing reader role role_definition_id = "50000000-aaaa-bbbb-cccc-100000000002" scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment?api-version=2019-10-01-preview" data = { - "properties": { - "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", - "principalId": object_id + "Properties": { + "RoleDefinitionId": f"{scope}/billingRoleDefinitions/{role_definition_id}", + "PrincipalId": object_id } } -resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp = requests.post(url=role_assignments_url, headers=management_header, json=data) resp.raise_for_status() diff --git a/Utilities/AzureSetup/MCA/eco_azure_full_access.sh b/Utilities/AzureSetup/MCA/eco_azure_full_access.sh index b883de3..b9965da 100644 --- a/Utilities/AzureSetup/MCA/eco_azure_full_access.sh +++ b/Utilities/AzureSetup/MCA/eco_azure_full_access.sh @@ -13,7 +13,7 @@ SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query passw SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') # Create service principal -az ad sp create --id $APP_ID +PRINCIPAL_ID=$(az ad sp create --id $APP_ID --output json --query id | tr -d '"') # Role assignments # assign reservation reader role @@ -37,8 +37,17 @@ az role assignment create --assignee $APP_ID --role "Savings plan Administrator" # assign cost management reader role az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" -# assign billing reader role -az role assignment create --assignee $APP_ID --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +# assign Billing Reader role using REST API +ROLE_DEF_ID="50000000-aaaa-bbbb-cccc-100000000002" +API_VERSION="2019-10-01-preview" +SCOPE="providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +ACCESS_TOKEN=$(az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv) +DATA='{\"Properties\": {\"RoleDefinitionId\": \"/${SCOPE}/billingRoleDefinitions/${ROLE_DEF_ID}\", \"PrincipalId\": \"${PRINCIPAL_ID}\"}}' +curl -X POST \ + "https://management.azure.com/${SCOPE}/createBillingRoleAssignment?api-version=${API_VERSION}" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -d "${DATA}" # Print registered app info diff --git a/Utilities/AzureSetup/MCA/eco_azure_readonly.ps1 b/Utilities/AzureSetup/MCA/eco_azure_readonly.ps1 index 3551ebb..55e5a0c 100644 --- a/Utilities/AzureSetup/MCA/eco_azure_readonly.ps1 +++ b/Utilities/AzureSetup/MCA/eco_azure_readonly.ps1 @@ -8,8 +8,9 @@ Connect-AzAccount -TenantId $tenantId # Register the app $app = New-AzADApplication -DisplayName $appName -# Create a service principal -New-AzADServicePrincipal -ApplicationId $app.AppId +# Create a service principal and get its ID +$sp = New-AzADServicePrincipal -ApplicationId $app.AppId +$principalId = $sp.Id # Delete all secret keys $secretKeys = Get-AzADAppCredential -ApplicationId $appId @@ -31,8 +32,25 @@ New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Savings plan Rea # assign cost management reader role New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Cost Management Reader" -Scope "/providers/Microsoft.Management/managementGroups/$tenantId" -# assign billing reader role -New-AzRoleAssignment -ApplicationId $appId -RoleDefinitionName "Billing Reader" -Scope "/providers/Microsoft.Billing/billingAccounts/$billingAccountId" +# assign billing reader role via REST API +$ROLE_DEF_ID = "50000000-aaaa-bbbb-cccc-100000000002" +$API_VERSION = "2019-10-01-preview" +$SCOPE = "providers/Microsoft.Billing/billingAccounts/$billingAccountId" +$ACCESS_TOKEN = az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv +$DATA = @{ + Properties = @{ + RoleDefinitionId = "/$SCOPE/billingRoleDefinitions/$ROLE_DEF_ID" + PrincipalId = $principalId + } +} | ConvertTo-Json +$headers = @{ + "Content-Type" = "application/json" + "Authorization" = "Bearer $ACCESS_TOKEN" +} +Invoke-RestMethod -Method Post ` + -Uri "https://management.azure.com/$SCOPE/createBillingRoleAssignment?api-version=$API_VERSION" ` + -Headers $headers ` + -Body $DATA # Output app details diff --git a/Utilities/AzureSetup/MCA/eco_azure_readonly.py b/Utilities/AzureSetup/MCA/eco_azure_readonly.py index 9d1cb47..ecb3c28 100644 --- a/Utilities/AzureSetup/MCA/eco_azure_readonly.py +++ b/Utilities/AzureSetup/MCA/eco_azure_readonly.py @@ -1,5 +1,6 @@ import requests from azure.identity import DefaultAzureCredential +import uuid # Set up the necessary variables TENANT_ID = "{{tenant_id}}" @@ -39,7 +40,7 @@ # assign reservation reader role role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -52,7 +53,7 @@ # assign cost management reader role role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -65,14 +66,14 @@ # assign billing reader role role_definition_id = "50000000-aaaa-bbbb-cccc-100000000002" scope = f"providers/Microsoft.Billing/billingAccounts/{BILLING_ACCOUNT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/createBillingRoleAssignment?api-version=2019-10-01-preview" data = { - "properties": { - "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", - "principalId": object_id + "Properties": { + "RoleDefinitionId": f"{scope}/billingRoleDefinitions/{role_definition_id}", + "PrincipalId": object_id } } -resp = requests.put(url=role_assignments_url, headers=management_header, json=data) +resp = requests.post(url=role_assignments_url, headers=management_header, json=data) resp.raise_for_status() diff --git a/Utilities/AzureSetup/MCA/eco_azure_readonly.sh b/Utilities/AzureSetup/MCA/eco_azure_readonly.sh index f5ed213..1002ee8 100644 --- a/Utilities/AzureSetup/MCA/eco_azure_readonly.sh +++ b/Utilities/AzureSetup/MCA/eco_azure_readonly.sh @@ -13,7 +13,7 @@ SECRET_KEY=$(az ad app credential reset --id $APP_ID --output json --query passw SECRET_KEY=$(echo $SECRET_KEY | tr -d '"') # Create service principal -az ad sp create --id $APP_ID +PRINCIPAL_ID=$(az ad sp create --id $APP_ID --output json --query id | tr -d '"') # Role assignments # assign reservation reader role @@ -25,8 +25,17 @@ az role assignment create --assignee $APP_ID --role "Savings plan Reader" --scop # assign cost management reader role az role assignment create --assignee $APP_ID --role "Cost Management Reader" --scope "/providers/Microsoft.Management/managementGroups/${TENANT_ID}" -# assign billing reader role -az role assignment create --assignee $APP_ID --role "Billing Reader" --scope "/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +# assign Billing Reader role using REST API +ROLE_DEF_ID="50000000-aaaa-bbbb-cccc-100000000002" +API_VERSION="2019-10-01-preview" +SCOPE="providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}" +ACCESS_TOKEN=$(az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv) +DATA='{\"Properties\": {\"RoleDefinitionId\": \"/${SCOPE}/billingRoleDefinitions/${ROLE_DEF_ID}\", \"PrincipalId\": \"${PRINCIPAL_ID}\"}}' +curl -X POST \ + "https://management.azure.com/${SCOPE}/createBillingRoleAssignment?api-version=${API_VERSION}" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -d "${DATA}" # Print registered app info diff --git a/Utilities/AzureSetup/PAYG/eco_azure_full_access.py b/Utilities/AzureSetup/PAYG/eco_azure_full_access.py index 0b87b75..2d22290 100644 --- a/Utilities/AzureSetup/PAYG/eco_azure_full_access.py +++ b/Utilities/AzureSetup/PAYG/eco_azure_full_access.py @@ -1,5 +1,6 @@ import requests from azure.identity import DefaultAzureCredential +import uuid # Set up the necessary variables TENANT_ID = "{{tenant_id}}" @@ -38,7 +39,7 @@ # assign reservation reader role role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -51,7 +52,7 @@ # assign reservation purchaser role role_definition_id = "f7b75c60-3036-4b75-91c3-6b41c27c1689" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -64,7 +65,7 @@ # assign reservation administrator role role_definition_id = "a8889054-8d42-49c9-bc1c-52486c10e7cd" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -77,7 +78,7 @@ # assign savings plan purchaser role role_definition_id = "3d24a3a0-c154-4f6f-a5ed-adc8e01ddb74" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -90,7 +91,7 @@ # assign savings plan administrator role role_definition_id = "433febaf-a31d-4d4f-8dc8-b4593b39bda5" scope = "providers/Microsoft.BillingBenefits" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -103,7 +104,7 @@ # assign cost management reader role role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", diff --git a/Utilities/AzureSetup/PAYG/eco_azure_readonly.py b/Utilities/AzureSetup/PAYG/eco_azure_readonly.py index 1d57a4e..b05ff46 100644 --- a/Utilities/AzureSetup/PAYG/eco_azure_readonly.py +++ b/Utilities/AzureSetup/PAYG/eco_azure_readonly.py @@ -1,5 +1,6 @@ import requests from azure.identity import DefaultAzureCredential +import uuid # Set up the necessary variables TENANT_ID = "{{tenant_id}}" @@ -38,7 +39,7 @@ # assign reservation reader role role_definition_id = "582fc458-8989-419f-a480-75249bc5db7e" scope = "providers/Microsoft.Capacity" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}", @@ -51,7 +52,7 @@ # assign cost management reader role role_definition_id = "72fafb9e-0641-4937-9268-a91bfd8191a3" scope = f"providers/Microsoft.Management/managementGroups/{TENANT_ID}" -role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{role_definition_id}?api-version=2022-04-01" +role_assignments_url = f"https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignments/{str(uuid.uuid4())}?api-version=2022-04-01" data = { "properties": { "roleDefinitionId": f"{scope}/providers/Microsoft.Authorization/roleDefinitions/{role_definition_id}",